Error Codes
Every error response from the Roam developer API includes a machine-readable error code. Branch on the code rather than parsing the human-readable message.
Response shape (v0 / Alpha API)
On /v0/... paths the human-readable sentence stays in error, and the catalog code is additive in code:
{
"error": "The bearer token is unknown, malformed, or expired; obtain a new token.",
"code": "invalid_token"
}
Clients that only read error continue to work. Prefer branching on code.
On /v1/... paths the body is {"ok": false, "error": "<code>"} — the catalog
code is the value of error (there is no separate code field). Full details
and success-envelope rules: Responses and Errors.
The catalog below is shared across v0 and v1.
HTTP status codes
| Status | Meaning |
|---|---|
400 Bad Request | Invalid request parameters or body |
401 Unauthorized | Missing or invalid authentication |
403 Forbidden | Valid auth but insufficient access or scope |
404 Not Found | Resource does not exist (or is invisible to the caller) |
405 Method Not Allowed | HTTP method not supported |
408 Request Timeout | Server timed out reading the request |
409 Conflict | Conflicts with current resource state |
410 Gone | Endpoint has been removed |
413 Payload Too Large | Request body or message text too large |
415 Unsupported Media Type | Content-Type not supported |
429 Too Many Requests | Rate limit exceeded |
500 Internal Server Error | Unexpected server failure; retrying may succeed |
501 Not Implemented | Operation not supported for the given input |
504 Gateway Timeout | Timed out waiting on an upstream service |
Some codes can appear with more than one status (for example a *_not_found code on a 400 when a referenced resource is missing). Always check both the status and the code field.
Catalog
Codes use Slack-style snake_case spellings where there is a well-known equivalent (ratelimited, not_authed, msg_too_long, …).
Generic (by status class)
| Code | Typical status | Description |
|---|---|---|
invalid_arguments | 400 | One or more request arguments are invalid or malformed |
invalid_auth | 401 | Authentication failed (generic) |
access_denied | 403 | Caller is not permitted to perform this action |
not_found | 404 | Resource could not be found (generic) |
method_not_allowed | 405 | HTTP method not supported |
request_timeout | 408 | Server timed out reading the request |
conflict | 409 | Conflicts with current resource state |
method_deprecated | 410 | Endpoint removed; see docs for replacement |
request_too_large | 413 | Request payload exceeds allowed size |
invalid_content_type | 415 | Content-Type not supported; use application/json |
ratelimited | 429 | Rate limit exceeded; retry after a delay |
internal_error | 500 | Unexpected server-side failure; retrying may succeed |
not_implemented | 501 | Operation not supported for the given input |
Request validation
| Code | Typical status | Description |
|---|---|---|
invalid_json | 400 | Request body is not valid JSON |
missing_parameter | 400 | A required parameter or field is missing |
invalid_parameter | 400 | A parameter or field has an invalid value |
invalid_cursor | 400 | Pagination cursor invalid or expired; restart without a cursor |
invalid_expand | 400 | An expand= field is not recognized |
cannot_unfurl_url | 400 | A requested URL is absent from the message or outside the app's registered unfurl domains |
Authentication and authorization
| Code | Typical status | Description |
|---|---|---|
not_authed | 401 | No bearer token was provided |
invalid_token | 401 | Token unknown, malformed, or expired; obtain a new token |
token_revoked | 401 | Token permanently unusable (e.g. owner archived); discard and re-authenticate — do not retry with the same token |
missing_scope | 403 | Token lacks a required scope; body includes needed (any-of) and provided |
access_mode_not_supported | 403 | Token access mode (personal vs organization) cannot use this endpoint |
not_in_chat | 403 | Caller is not a member of the specified chat |
not_in_group | 403 | Caller is not a member of the specified group |
user_not_in_chat | 400 | The targeted user is not a member of the specified chat (e.g. /chat.postEphemeral to a non-member) |
invalid_token and token_revoked responses also include:
WWW-Authenticate: Bearer error="invalid_token"
token_revoked vs invalid_token: both are 401, but token_revoked means
the credential is permanently dead (owner archived/deleted, client archived).
Do not retry with the same token — discard it and re-authenticate. See also
Authentication failures.
Resource not found
| Code | Typical status | Description |
|---|---|---|
chat_not_found | 404 | Chat could not be found |
user_not_found | 404 | User could not be found |
group_not_found | 404 | Group could not be found |
meeting_not_found | 404 | Meeting could not be found |
meeting_link_not_found | 404 | Meeting link could not be found |
message_not_found | 404 | Message could not be found |
scheduled_message_not_found | 404 | Scheduled message could not be found (or was scheduled by a different credential) |
event_not_found | 404 | On-Air event could not be found |
guest_not_found | 404 | On-Air guest could not be found |
address_not_found | 404 | Address could not be found |
lobby_not_found | 404 | Lobby could not be found |
magicast_not_found | 404 | Magicast could not be found |
transcript_not_found | 404 | Meeting has no transcript, or it is not yet available |
recording_not_found | 404 | Recording could not be found |
webhook_not_found | 404 | Webhook subscription could not be found |
item_not_found | 404 | Item could not be found |
asset_not_found | 404 | Asset could not be found |
handle_not_found | 404 | Handle could not be found |
session_not_found | 404 | Agent session could not be found (reserved) |
connection_not_found | 404 | Agent connection could not be found (reserved) |
file_not_found | 404 | File could not be found (reserved) |
stream_not_found | 404 | Stream could not be found |
Message and resource conditions
| Code | Typical status | Description |
|---|---|---|
msg_too_long | 413 | Message text exceeds maximum length; truncate and retry |
name_taken | 400 | A resource with that name already exists |
is_archived | 400 | Target resource is archived and cannot be modified |
scheduled_message_already_sent | 409 | Scheduled message was already sent (or delivery has begun) and can no longer be canceled |
Transcripts and search
| Code | Typical status | Description |
|---|---|---|
transcript_pending | 404 | Transcript not ready yet (meeting in progress or still processing); retry later (may include Retry-After) |
transcript_unavailable | 404 | Meeting was not transcribed; stop retrying |
upstream_timeout | 504 | Timed out waiting on an upstream service; retry |
Client guidance
- Branch on
code, not on free-texterrorstrings. - Treat
token_revokedas terminal for that credential — discard it and run your re-auth flow. - Treat
transcript_unavailableas terminal for that meeting; treattranscript_pendingas retryable. - For
ratelimited(429), honorRetry-Afterand use exponential backoff. See Rate Limiting. - For
missing_scope(403), readneeded(any-of array) andprovided(granted scopes, post-alias) to decide what to request on the next authorization — do not parse the human-readable message. See Responses and Errors.