> ## Documentation Index
> Fetch the complete documentation index at: https://developers.workchats.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> The error envelope, and every code

## Envelope

Every non-2xx response has the same shape:

```json theme={null}
{ "ok": false, "error": { "code": "conversation_not_found", "message": "…" }, "request_id": "…" }
```

`code` is stable and machine-readable. Branch your handling on it, not on
`message`, which is for humans and can change wording. `request_id` is also
sent as the `X-Request-Id` header — include it if you contact
[support@workchats.com](mailto:support@workchats.com) about a specific failed call.

## Forward compatibility

The API is additive-only within `/v1` (see [Versioning](/policies/versioning)).
New error codes can be added over time. Treat an unknown code as a generic
failure, not a crash: a code is never removed or renamed within `/v1`.

## Every code

| HTTP | Code | Meaning |
| - | - | - |
| 400 | `invalid_request` | A parameter is missing or malformed |
| 400 | `invalid_blocks` | A block failed validation — see [Blocks](/guides/blocks) |
| 400 | `invalid_cursor` | A malformed pagination cursor |
| 400 | `missing_idempotency_key` | `POST /v1/messages` without `Idempotency-Key` |
| 400 | `token_in_query` | The token was sent as a query parameter instead of the `Authorization` header |
| 400 | `invalid_grant` | OAuth: the code is unknown, expired, used, or bound to another App |
| 400 | `unsupported_grant_type` | OAuth: anything but `authorization_code` (there's no refresh grant) |
| 401 | `not_authed` | No `Authorization` header |
| 401 | `invalid_auth` | The token doesn't parse or doesn't exist |
| 401 | `token_revoked` | The installation is disconnected, uninstalled, or the App is disabled |
| 401 | `invalid_client` | OAuth: `client_id` / `client_secret` didn't authenticate |
| 403 | `missing_scope` | The token lacks a scope the call needs |
| 403 | `cannot_dm_user` | That person can't receive a DM from your bot |
| 403 | `not_in_conversation` | Your bot was a member of that group or channel and was removed |
| 403 | `not_allowed_in_conversation` | That channel is announcement-only |
| 404 | `user_not_found` | No such user, or their email is hidden from your bot |
| 404 | `conversation_not_found` | No such conversation, or your bot was never a member of it |
| 404 | `message_not_found` | No such message, it's already deleted, or your bot didn't send it |
| 404 | `file_not_found` | No message your bot can read in the conversation carries the file, or it was deleted |
| 404 | `not_found` | Generic — no more specific code applied |
| 406 | `not_acceptable` | Your `Accept` header excludes `application/json` |
| 409 | `conversation_archived` | The group is archived |
| 409 | `user_deactivated` | The person is deactivated or removed |
| 413 | `payload_too_large` | Request body too large |
| 415 | `unsupported_media_type` | Body isn't `application/json` |
| 422 | `idempotency_key_reused` | Same `Idempotency-Key`, different body — or the original message was edited or deleted |
| 429 | `rate_limited` | Over a rate limit — see the `Retry-After` header |
| 500 | `internal_error` | Something went wrong on our side |
| any 4xx | `client_error` | A raised error with no more specific code; carries the response's own status |
| any 5xx | `server_error` | Same, for a 5xx |

`token_expired` is reserved for if and when refresh tokens are added — bot
tokens don't currently expire, so you won't see it.
