Error codes
Every error response has one shape: { error: { type, code, message, param, doc_url, request_id, details? } }. Branch on type for the class of failure and on code for the exact reason; meanings never change. retry says whether repeating can help: never (fix the request), after_delay (wait for Retry-After, then send it again unchanged), same_key (send it again with the same Idempotency-Key) or refetch_then_retry (read the resource again first).
| code | status | type | retry | meaning |
|---|---|---|---|---|
acknowledgement_required | 409 | conflict | never | The change set has flags (`plan.flags`) the confirmation did not acknowledge. |
ai_assistant_disabled | 403 | permission | never | |
ai_draft_rejected | 422 | invalid_request | never | |
ai_drafting_disabled | 403 | permission | never | |
ai_unavailable | 503 | api_error | after_delay | |
alias_conflict | 409 | conflict | never | |
already_exists | 409 | conflict | never | |
anonymous_id_retired | 403 | permission | never | |
asset_processing_unavailable | 503 | api_error | after_delay | |
asset_quota_exceeded | 409 | conflict | never | |
audit_reason_required | 403 | permission | never | |
authentication_required | 401 | authentication | never | No credential was sent. Send `Authorization: Bearer <key or token>`. |
batch_too_large | 400 | invalid_request | never | More than 100 events (25 on customer routes) in one request; nothing was stored. |
battle_pass_paused | 409 | conflict | never | |
battle_pass_unavailable | 409 | conflict | never | |
billing_unavailable | 503 | api_error | never | |
body_too_large | 413 | invalid_request | never | |
capability_unavailable | 501 | api_error | never | |
change_set_expired | 409 | conflict | never | The change set is past its `expires_at` (24 hours after it was proposed). Propose it again. |
change_set_not_pending | 409 | conflict | never | The change set was already committed, cancelled, stale or expired. |
claim_invalid | 404 | not_found | never | |
concurrent_update | 409 | conflict | same_key | A concurrent change got there first. Send the same request again with the same key after `Retry-After`. |
config_format_required | 409 | conflict | never | A v1 configuration export was asked for an environment that needs v2. Request `schema=orishare.config/v2`. |
confirmation_required | 403 | permission | never | Only a signed-in owner or admin confirming in the dashboard commits a change set. Hand over the change set id; never retry with a token or key. |
connector_order_pending | 503 | api_error | after_delay | |
contribution_conflict | 409 | conflict | never | |
conversation_expired | 409 | conflict | never | |
conversation_full | 409 | conflict | never | |
credentials_revoked | 401 | authentication | never | |
csrf_invalid | 403 | permission | never | |
customer_admission_unavailable | 503 | api_error | after_delay | |
customer_credential_retired | 410 | authentication | never | A legacy browser credential (anonymous_id or an HMAC customer token) was used after activation. Use customer sessions. |
customer_session_limit | 409 | conflict | after_delay | The session family already has four live tokens, so a renewal or an embed (hosted widget) exchange in it is refused. Your own renewal: wait for a token of the family to expire or revoke one you no longer use, then renew, or exchange again for a new family. The hosted widget: hand it a freshly minted token that nothing else uses (a new family). |
customer_surface_inactive | 403 | permission | never | The environment's customer surface is not activated yet; customer sessions cannot be created. |
energy_archived | 409 | conflict | never | |
energy_paused | 409 | conflict | never | |
entitlement_required | 403 | permission | never | |
environment_activating | 503 | api_error | after_delay | |
environment_required | 403 | permission | never | A personal access token or dashboard session called an environment route without `X-Orishare-Environment`. |
environment_served_elsewhere | 421 | invalid_request | never | |
evaluation_failed | 502 | api_error | same_key | |
event_id_conflict | 409 | conflict | never | The event id was already used with a different payload or for another customer. Never resend it with a new body; use a new id for a new event. |
event_requires_secret_key | 403 | permission | never | The event is not `client_allowed`; only a backend with a secret key may send it. |
event_too_large | 413 | invalid_request | never | |
forbidden | 403 | permission | never | The credential is valid but its kind or role may not do this: for example a management route called with an API key instead of a personal access token or dashboard session, a publishable key or customer session on a route for secret keys or members, or a member who is not an owner or admin. |
free_allowance_exhausted | 402 | rate_limited | never | |
idempotency_in_progress | 409 | idempotency | after_delay | A request with the same key is still running. Wait for `Retry-After` and send it again unchanged. |
idempotency_key_required | 400 | idempotency | never | The route needs an `Idempotency-Key` header. |
idempotency_key_reused | 422 | idempotency | never | The `Idempotency-Key` was already used with a different body. Use a new key for a new intent. |
insufficient_balance | 409 | conflict | never | |
insufficient_customer_scope | 403 | permission | never | The customer session lacks the capability the route needs (`customer:read`, `customer:write` or `events:write_client`, after the grant policy and the keys narrowed it), or the publishable key cannot exchange or renew sessions (`customer_sessions:exchange`). Mint without narrowing `capabilities`, or use a key with that scope. |
insufficient_energy | 409 | conflict | never | |
insufficient_items | 409 | conflict | never | |
insufficient_scope | 403 | permission | never | The credential lacks a scope the route needs; `details.required` names it. Ask the operator for a key or token with that scope (GET /v1/principal shows what you hold). |
internal_error | 500 | api_error | never | |
invalid_credentials | 401 | authentication | never | The `Authorization` header is malformed (not `Bearer <token>`) or names an unknown key or token. Check the value and its environment; a revoked one answers `credentials_revoked`. |
invalid_cursor | 400 | invalid_request | never | |
invalid_customer_session | 401 | authentication | never | The customer token is missing, expired or revoked (including after logout or a merge). Exchange again. |
invalid_economy_request | 409 | conflict | never | |
invalid_json | 400 | invalid_request | never | |
invalid_session_grant | 400 | invalid_request | never | The exchange was refused: an invalid or expired provider token, a wrong audience or issuer, or an unknown customer. |
invalid_state | 409 | conflict | never | |
inventory_full | 409 | conflict | never | |
item_archived | 409 | conflict | never | |
last_owner | 409 | conflict | never | |
live_requires_claim | 403 | permission | never | |
loot_table_unavailable | 409 | conflict | never | |
match_profile_archived | 409 | conflict | never | |
merge_capacity_exceeded | 409 | conflict | never | |
merge_conflict | 409 | conflict | never | |
mid_period_change_unsupported | 409 | conflict | never | |
not_equippable | 409 | conflict | never | |
occurred_at_in_future | 400 | invalid_request | never | `occurred_at` is more than 24 hours in the future. |
offer_changed | 409 | conflict | refetch_then_retry | |
offer_unavailable | 409 | conflict | refetch_then_retry | |
operation_running | 409 | conflict | after_delay | |
origin_mismatch | 403 | permission | never | A dashboard-only action came from another origin: committing a change set (`POST /v1/change_set_commits`) must come from the signed-in dashboard on the dashboard's own origin. Unlike `origin_not_allowed`, which is about a browser integration's origin missing from the client configuration's `allowed_origins`, this cannot be fixed by configuration: confirm in the dashboard. |
origin_not_allowed | 403 | permission | never | The request's `Origin` is not in the environment's allowed origins. |
playbook_refused | 409 | conflict | never | The playbook preview was refused; `details.refusals` lists missing events, weaker trust, key collisions and parameter bounds. |
playbook_too_large | 413 | invalid_request | never | |
portal_unavailable | 503 | api_error | never | The hosted widget cannot start a session: it is not enabled for this deployment, or the portal is unknown in this environment, disabled or has no published version. Publish or enable the portal; check that the publishable key belongs to its environment. |
portal_version_stale | 409 | conflict | refetch_then_retry | |
premium_required | 409 | conflict | never | |
preview_stale | 409 | conflict | never | The configuration or a planned resource changed after the change set was previewed. Propose the same commands again. |
pricing_unavailable | 503 | api_error | never | |
proof_of_work_invalid | 422 | invalid_request | never | |
purchase_limit_reached | 409 | conflict | never | |
quota_exceeded | 429 | rate_limited | after_delay | |
rate_limited | 429 | rate_limited | after_delay | Too many requests. Wait for `Retry-After`; the request did not run. |
resource_in_use | 409 | conflict | never | |
resource_not_found | 404 | not_found | never | No such resource in this environment (also for ids of other environments). For events, poll by the `evt_` id, not your business id. |
reward_unavailable | 409 | conflict | never | |
route_not_found | 404 | not_found | never | |
rule_invalid | 422 | invalid_request | never | The rule definition was refused; `details.errors` lists `rule_invalid.*` codes with paths, such as `rule_invalid.action_reference` for an unknown program key or `rule_invalid.capability_unavailable`. |
selection_empty | 409 | conflict | never | |
selection_too_large | 400 | invalid_request | never | |
selection_unsupported | 409 | conflict | never | |
source_time_untrusted | 400 | invalid_request | never | |
stale_write | 409 | conflict | refetch_then_retry | The revision you sent (`If-Match` or `expected_revision`) is no longer current, or it is missing where required. Read the resource again and decide anew. |
streak_repair_limit_reached | 409 | conflict | never | |
streak_repair_unavailable | 409 | conflict | never | |
test_environment_resetting | 409 | conflict | after_delay | |
test_player_limit | 403 | permission | never | |
tier_already_claimed | 409 | conflict | never | |
tier_not_reached | 409 | conflict | never | |
too_many_boosts | 409 | conflict | never | |
trust_insufficient | 403 | permission | never | |
turn_in_progress | 409 | conflict | after_delay | |
unsupported_media_type | 415 | invalid_request | never | |
usage_allowance_exhausted | 402 | rate_limited | never | |
validation_failed | 400 | invalid_request | never | The request did not match the schema or a rule of the route; `param` names the field. Fix the request. |