Errors share one shape: an HTTP status, a stable machine `code`, a human `message`, and the `request_id` of the call (also returned in the `x-request-id` header — quote it in support tickets).
```responses
~~~404
{
"error": { "code": "summary.not_found", "message": "Summary not found" },
"request_id": "5f0c…"
}
~~~
```
> Codes are contracts: stable strings you can branch on. Messages are for humans and may change — never parse them.
## Registry
### Auth & gating
| Code | Status | When |
|---|---|---|
| `auth.no_key` | 401 | Keyed endpoint called without an `Authorization` header |
| `auth.invalid_key` | 401 | Malformed, unknown, or revoked key — uniform, no oracle |
| `auth.unauthenticated` | 401 | `include_account_feeds` present on `/v1/summarize` without a key |
| `subscription.api_not_enabled` | 403 | Plan lacks API access — every keyed endpoint |
| `subscription.inactive` | 403 | Chat send with an inactive subscription |
### Validation & routing
| Code | Status | When |
|---|---|---|
| `validation.invalid_input` | 400 | Malformed ids, bounds, tri-states, unknown actions |
| `validation.body_too_large` | 400 | Body exceeds 256KB (400, not 413) |
| `route.not_found` | 404 | Nonexistent route |
| `internal.error` | 500 | Unhandled failure — sanitized; internals go to telemetry only |
### Rate limits
| Code | Status | When |
|---|---|---|
| `rate_limit.interval` | 429 | Second call < 3s after the previous (+ exact `Retry-After`) |
| `rate_limit.hourly_budget` | 429 | 500/hour exhausted |
| `rate_limit.ip_per_min` | 429 | Anonymous 30/min/IP exhausted |
| `rate_limit.free_tier_daily` | 429 | Keyless daily/global free-tier caps |
| `rate_limit.daily_messages` | 429 | Chat daily message cap |
| `rate_limit.resend_window` | 429 | Email verification resend inside the 5-minute window |
| `rate_limit.support_messages` | 429 | Support message 10-per-10-min user cap (+ `Retry-After`) |
### Summaries
| Code | Status | When |
|---|---|---|
| `summary.not_found` | 404 | Foreign or unknown briefing — uniform across internal, share, and keyless-run ids |
| `summary.not_completed` | 400 | Sharing a briefing that isn't completed successfully |
| `summary.not_saveable` | 400 | Saving a briefing with no content |
| `summary.not_shared` | 400 | Unsharing a briefing that has no share id |
| `summary.already_sent` | 409 | send-early on a delivered briefing |
| `summary.delivery_in_progress` | 409 | send-early while delivery is mid-flight |
| `summary.not_automatic` | 422 | send-early on a non-automatic briefing |
| `summary.not_ready` | 422 | send-early on a briefing still generating |
| `summary.dispatch_failed` | 502 | The generation workflow failed to dispatch |
### Summarize
| Code | Status | When |
|---|---|---|
| `summarize.no_input_sources` | 400 | Keyed trigger with no inputs |
| `api.run_in_progress` | 409 | A previous on-demand run is still in flight |
### Chat
| Code | Status | When |
|---|---|---|
| `chat.turn_in_progress` | 409 | A live turn is running |
| `chat.unresolved_failed_turn` | 409 | A prior failed turn needs retry or delete first |
| `chat.not_retryable` | 409 | Retry on a turn that didn't fail |
| `chat.no_pending_action` | 409 | Approve/deny with nothing pending |
| `chat.compacted` | 400 | Message is part of a saved summary (compaction boundary) |
| `chat.compaction_in_progress` | 409 | A compaction roll is in flight |
| `chat.not_last` | 400 | Edit/delete target isn't the latest exchange |
| `chat.message_not_found` | 404 | Foreign or unknown message id |
| `chatbot.disabled` | 403 | Chatbot is disabled for the account |
### Feeds
| Code | Status | When |
|---|---|---|
| `feed.missing_params` | 400 | `url_or_handle` absent |
| `feed.invalid_type` | 400 | Unknown platform value |
| `feed.type_required` | 400 | Platform not derivable from a bare handle |
| `feed.already_added` | 400 | Duplicate feed |
| `feed.invalid_status` | 400 | PATCH status not `active`/`disabled` |
| `feed.invalid_transition` | 400 | Transition out of `resolving` (locked) |
| `feed.limit_reached` | 400 | Plan feed cap |
| `feed.platform_not_in_plan` | 400 | Platform not in the plan |
| `feed.platform_unavailable` | 400 | Platform globally unavailable |
| `feed.not_found` | 404 | Foreign or unknown feed |
### Channels
| Code | Status | When |
|---|---|---|
| `channel.invalid_email` | 400 | Add email fails validation |
| `channel.email_in_use` | 400 | Address active on another account |
| `channel.invalid_url` | 400 | Webhook URL fails https/host/no-credentials/≤2048 (+ SSRF block) |
| `channel.invalid_code` | 400 | Verification code wrong or expired |
| `channel.invalid_status` | 400 | PATCH status not `active`/`disabled` |
| `channel.invalid_transition` | 400 | Illegal status transition (e.g. off `pending_verification`) |
| `channel.action_not_supported` | 400 | e.g. test on a non-webhook channel |
| `channel.not_found` | 404 | Foreign or unknown channel |
| `channel.not_pending` | 400 | Resend-verification on a non-pending channel |
| `channel.not_verified` | 400 | Status toggle before verification |
| `channel.not_patchable` | 400 | PATCH on a web-push channel |
| `channel.verification_pending` | 400 | Telegram handshake incomplete |
| `channel.type_not_in_plan` | 403 | Plan lacks this channel type |
| `channel.limit_reached` | 403 | Plan channel cap |
| `channel.webhook_test_failed` | 400 | Test delivery failed (unreachable / non-2xx / timeout) |
### Manage & settings
| Code | Status | When |
|---|---|---|
| `manage.invalid_time` | 400 | `daily_time` not strict `HH:MM` (24h) |
| `manage.invalid_timezone` | 400 | Timezone not in the supported set |
| `manage.invalid_language` | 400 | Language not in the supported set |
| `manage.field_read_only` | 400 | Writing a read-only field (chatbot language; `trigger_early_hours`) |
### Subscription & help
| Code | Status | When |
|---|---|---|
| `subscription.unknown_plan` | 400 | `plan_id` not in the catalog |
| `subscription.card_required` | 409 | Upgrade with no card on file |
| `subscription.cross_group_not_allowed` | 409 | Upgrade across plan groups |
| `subscription.not_an_upgrade` | 409 | Target rank not strictly higher |
| `attachments.invalid` | 400 | Malformed attachment paths or multipart request exceeding the attachment budget |
| `attachments.too_many` | 400 | More than 5 attachments |
| `help.ticket_not_found` | 404 | Foreign or unknown ticket |
| `help.ticket_closed` | 400 | Reply/close on a closed ticket |
| `help.request_not_found` | 404 | Foreign or unknown feature request |
| `help.request_not_withdrawable` | 400 | Withdraw on a non-open request |
### Operational
| Code | Status | When |
|---|---|---|
| `deploy.in_progress` | 503 | Brief window during service deploys (+ `Retry-After`) |
Errors
Errors share one shape: an HTTP status, a stable machine code, a human message, and the request_id of the call (also returned in the x-request-id header — quote it in support tickets).
~~~404
{
"error": { "code": "summary.not_found", "message": "Summary not found" },
"request_id": "5f0c…"
}
~~~
Codes are contracts: stable strings you can branch on. Messages are for humans and may change — never parse them.
Registry
Auth & gating
| Code | Status | When |
|---|---|---|
| auth.no_key | 401 | Keyed endpoint called without an Authorization header |
| auth.invalid_key | 401 | Malformed, unknown, or revoked key — uniform, no oracle |
| auth.unauthenticated | 401 | include_account_feeds present on /v1/summarize without a key |
| subscription.api_not_enabled | 403 | Plan lacks API access — every keyed endpoint |
| subscription.inactive | 403 | Chat send with an inactive subscription |
Validation & routing
| Code | Status | When |
|---|---|---|
| validation.invalid_input | 400 | Malformed ids, bounds, tri-states, unknown actions |
| validation.body_too_large | 400 | Body exceeds 256KB (400, not 413) |
| route.not_found | 404 | Nonexistent route |
| internal.error | 500 | Unhandled failure — sanitized; internals go to telemetry only |
Rate limits
| Code | Status | When |
|---|---|---|
| rate_limit.interval | 429 | Second call < 3s after the previous (+ exact Retry-After) |
| rate_limit.hourly_budget | 429 | 500/hour exhausted |
| rate_limit.ip_per_min | 429 | Anonymous 30/min/IP exhausted |
| rate_limit.free_tier_daily | 429 | Keyless daily/global free-tier caps |
| rate_limit.daily_messages | 429 | Chat daily message cap |
| rate_limit.resend_window | 429 | Email verification resend inside the 5-minute window |
| rate_limit.support_messages | 429 | Support message 10-per-10-min user cap (+ Retry-After) |
Summaries
| Code | Status | When |
|---|---|---|
| summary.not_found | 404 | Foreign or unknown briefing — uniform across internal, share, and keyless-run ids |
| summary.not_completed | 400 | Sharing a briefing that isn't completed successfully |
| summary.not_saveable | 400 | Saving a briefing with no content |
| summary.not_shared | 400 | Unsharing a briefing that has no share id |
| summary.already_sent | 409 | send-early on a delivered briefing |
| summary.delivery_in_progress | 409 | send-early while delivery is mid-flight |
| summary.not_automatic | 422 | send-early on a non-automatic briefing |
| summary.not_ready | 422 | send-early on a briefing still generating |
| summary.dispatch_failed | 502 | The generation workflow failed to dispatch |
Summarize
| Code | Status | When |
|---|---|---|
| summarize.no_input_sources | 400 | Keyed trigger with no inputs |
| api.run_in_progress | 409 | A previous on-demand run is still in flight |
Chat
| Code | Status | When |
|---|---|---|
| chat.turn_in_progress | 409 | A live turn is running |
| chat.unresolved_failed_turn | 409 | A prior failed turn needs retry or delete first |
| chat.not_retryable | 409 | Retry on a turn that didn't fail |
| chat.no_pending_action | 409 | Approve/deny with nothing pending |
| chat.compacted | 400 | Message is part of a saved summary (compaction boundary) |
| chat.compaction_in_progress | 409 | A compaction roll is in flight |
| chat.not_last | 400 | Edit/delete target isn't the latest exchange |
| chat.message_not_found | 404 | Foreign or unknown message id |
| chatbot.disabled | 403 | Chatbot is disabled for the account |
Feeds
| Code | Status | When |
|---|---|---|
| feed.missing_params | 400 | url_or_handle absent |
| feed.invalid_type | 400 | Unknown platform value |
| feed.type_required | 400 | Platform not derivable from a bare handle |
| feed.already_added | 400 | Duplicate feed |
| feed.invalid_status | 400 | PATCH status not active/disabled |
| feed.invalid_transition | 400 | Transition out of resolving (locked) |
| feed.limit_reached | 400 | Plan feed cap |
| feed.platform_not_in_plan | 400 | Platform not in the plan |
| feed.platform_unavailable | 400 | Platform globally unavailable |
| feed.not_found | 404 | Foreign or unknown feed |
Channels
| Code | Status | When |
|---|---|---|
| channel.invalid_email | 400 | Add email fails validation |
| channel.email_in_use | 400 | Address active on another account |
| channel.invalid_url | 400 | Webhook URL fails https/host/no-credentials/≤2048 (+ SSRF block) |
| channel.invalid_code | 400 | Verification code wrong or expired |
| channel.invalid_status | 400 | PATCH status not active/disabled |
| channel.invalid_transition | 400 | Illegal status transition (e.g. off pending_verification) |
| channel.action_not_supported | 400 | e.g. test on a non-webhook channel |
| channel.not_found | 404 | Foreign or unknown channel |
| channel.not_pending | 400 | Resend-verification on a non-pending channel |
| channel.not_verified | 400 | Status toggle before verification |
| channel.not_patchable | 400 | PATCH on a web-push channel |
| channel.verification_pending | 400 | Telegram handshake incomplete |
| channel.type_not_in_plan | 403 | Plan lacks this channel type |
| channel.limit_reached | 403 | Plan channel cap |
| channel.webhook_test_failed | 400 | Test delivery failed (unreachable / non-2xx / timeout) |
Manage & settings
| Code | Status | When |
|---|---|---|
| manage.invalid_time | 400 | daily_time not strict HH:MM (24h) |
| manage.invalid_timezone | 400 | Timezone not in the supported set |
| manage.invalid_language | 400 | Language not in the supported set |
| manage.field_read_only | 400 | Writing a read-only field (chatbot language; trigger_early_hours) |
Subscription & help
| Code | Status | When |
|---|---|---|
| subscription.unknown_plan | 400 | plan_id not in the catalog |
| subscription.card_required | 409 | Upgrade with no card on file |
| subscription.cross_group_not_allowed | 409 | Upgrade across plan groups |
| subscription.not_an_upgrade | 409 | Target rank not strictly higher |
| attachments.invalid | 400 | Malformed attachment paths or multipart request exceeding the attachment budget |
| attachments.too_many | 400 | More than 5 attachments |
| help.ticket_not_found | 404 | Foreign or unknown ticket |
| help.ticket_closed | 400 | Reply/close on a closed ticket |
| help.request_not_found | 404 | Foreign or unknown feature request |
| help.request_not_withdrawable | 400 | Withdraw on a non-open request |
Operational
| Code | Status | When |
|---|---|---|
| deploy.in_progress | 503 | Brief window during service deploys (+ Retry-After) | Copy page ▾ Guides / Errors
Errors Every machine error code the API can return.
Errors share one shape: an HTTP status, a stable machine code, a human message, and the request_id of the call (also returned in the x-request-id header — quote it in support tickets).
404
{
"error": { "code": "summary.not_found", "message": "Summary not found" },
"request_id": "5f0c…"
} Copy { "error" : { "code" : "summary.not_found" , "message" : "Summary not found" }, "request_id" : "5f0c…" }
Codes are contracts: stable strings you can branch on. Messages are for humans and may change — never parse them.
Registry
Auth & gating
Code Status When auth.no_key401 Keyed endpoint called without an Authorization header auth.invalid_key401 Malformed, unknown, or revoked key — uniform, no oracle auth.unauthenticated401 include_account_feeds present on /v1/summarize without a keysubscription.api_not_enabled403 Plan lacks API access — every keyed endpoint subscription.inactive403 Chat send with an inactive subscription
Validation & routing
Code Status When validation.invalid_input400 Malformed ids, bounds, tri-states, unknown actions validation.body_too_large400 Body exceeds 256KB (400, not 413) route.not_found404 Nonexistent route internal.error500 Unhandled failure — sanitized; internals go to telemetry only
Rate limits
Code Status When rate_limit.interval429 Second call < 3s after the previous (+ exact Retry-After) rate_limit.hourly_budget429 500/hour exhausted rate_limit.ip_per_min429 Anonymous 30/min/IP exhausted rate_limit.free_tier_daily429 Keyless daily/global free-tier caps rate_limit.daily_messages429 Chat daily message cap rate_limit.resend_window429 Email verification resend inside the 5-minute window rate_limit.support_messages429 Support message 10-per-10-min user cap (+ Retry-After)
Summaries
Code Status When summary.not_found404 Foreign or unknown briefing — uniform across internal, share, and keyless-run ids summary.not_completed400 Sharing a briefing that isn't completed successfully summary.not_saveable400 Saving a briefing with no content summary.not_shared400 Unsharing a briefing that has no share id summary.already_sent409 send-early on a delivered briefing summary.delivery_in_progress409 send-early while delivery is mid-flight summary.not_automatic422 send-early on a non-automatic briefing summary.not_ready422 send-early on a briefing still generating summary.dispatch_failed502 The generation workflow failed to dispatch
Summarize
Code Status When summarize.no_input_sources400 Keyed trigger with no inputs api.run_in_progress409 A previous on-demand run is still in flight
Chat
Code Status When chat.turn_in_progress409 A live turn is running chat.unresolved_failed_turn409 A prior failed turn needs retry or delete first chat.not_retryable409 Retry on a turn that didn't fail chat.no_pending_action409 Approve/deny with nothing pending chat.compacted400 Message is part of a saved summary (compaction boundary) chat.compaction_in_progress409 A compaction roll is in flight chat.not_last400 Edit/delete target isn't the latest exchange chat.message_not_found404 Foreign or unknown message id chatbot.disabled403 Chatbot is disabled for the account
Feeds
Code Status When feed.missing_params400 url_or_handle absentfeed.invalid_type400 Unknown platform value feed.type_required400 Platform not derivable from a bare handle feed.already_added400 Duplicate feed feed.invalid_status400 PATCH status not active/disabled feed.invalid_transition400 Transition out of resolving (locked) feed.limit_reached400 Plan feed cap feed.platform_not_in_plan400 Platform not in the plan feed.platform_unavailable400 Platform globally unavailable feed.not_found404 Foreign or unknown feed
Channels
Code Status When channel.invalid_email400 Add email fails validation channel.email_in_use400 Address active on another account channel.invalid_url400 Webhook URL fails https/host/no-credentials/≤2048 (+ SSRF block) channel.invalid_code400 Verification code wrong or expired channel.invalid_status400 PATCH status not active/disabled channel.invalid_transition400 Illegal status transition (e.g. off pending_verification) channel.action_not_supported400 e.g. test on a non-webhook channel channel.not_found404 Foreign or unknown channel channel.not_pending400 Resend-verification on a non-pending channel channel.not_verified400 Status toggle before verification channel.not_patchable400 PATCH on a web-push channel channel.verification_pending400 Telegram handshake incomplete channel.type_not_in_plan403 Plan lacks this channel type channel.limit_reached403 Plan channel cap channel.webhook_test_failed400 Test delivery failed (unreachable / non-2xx / timeout)
Manage & settings
Code Status When manage.invalid_time400 daily_time not strict HH:MM (24h)manage.invalid_timezone400 Timezone not in the supported set manage.invalid_language400 Language not in the supported set manage.field_read_only400 Writing a read-only field (chatbot language; trigger_early_hours)
Subscription & help
Code Status When subscription.unknown_plan400 plan_id not in the catalogsubscription.card_required409 Upgrade with no card on file subscription.cross_group_not_allowed409 Upgrade across plan groups subscription.not_an_upgrade409 Target rank not strictly higher attachments.invalid400 Malformed attachment paths or multipart request exceeding the attachment budget attachments.too_many400 More than 5 attachments help.ticket_not_found404 Foreign or unknown ticket help.ticket_closed400 Reply/close on a closed ticket help.request_not_found404 Foreign or unknown feature request help.request_not_withdrawable400 Withdraw on a non-open request
Operational
Code Status When deploy.in_progress503 Brief window during service deploys (+ Retry-After)