Errors
Every error response uses a documented type and HTTP status. The first recommended fix is listed below; the dashboard surfaces the full fix list per error type.
Always include the X-Request-ID. Every error response carries an
X-Request-ID header. Include that value when contacting support about a failed request.
| Status | Type | Description | First fix |
|---|---|---|---|
| 401 | auth_error |
The request is missing an API key, the key is malformed, or it does not exist in the configured user list. | Send your Boltch key as `Authorization: Bearer YOUR_BOLTCH_KEY` or as the `x-api-key` header. |
| 400 | invalid_request |
The request body is malformed, missing a required field, or contains an invalid parameter value. | Verify the JSON body parses cleanly and matches the endpoint schema shown in the Documentation tab. |
| 400 | not_supported |
The requested model is valid but the authenticated user's tier does not include access to it. | Pick a model from `GET /v1/models`, which only lists models your tier can call. |
| 403 | forbidden |
The signed-in account is authenticated but does not have the role required for this workspace action. | Ask a workspace owner or admin to perform the action, or to raise your role to admin. |
| 401 | unauthenticated |
The request did not carry a signed-in Boltch session. | Sign in to the Boltch dashboard and retry. |
| 500 | workspace_write_failed |
A workspace, membership, or invite write could not be committed. No partial change was applied. | Retry the request; the operation is transactional and safe to repeat. |
| 404 | invite_used |
This workspace invite has already been accepted and cannot be reused. | Ask the workspace admin to send you a new invite. |
| 404 | invite_expired |
This workspace invite has passed its expiry window and is no longer valid. | Ask the workspace admin to send a fresh invite. |
| 404 | already_member |
This account is already a member of the workspace the invite pointed to. | Switch to the workspace from the dashboard switcher instead of accepting again. |
| 404 | not_found |
The requested endpoint path or model id does not exist on this Worker. | Double-check the URL path against the Documentation tab (for example `/v1/chat/completions`). |
| 413 | payload_too_large |
The request body exceeds the per-request size limit enforced by the Worker. | Trim prompts, system messages, or attached context until the body size fits under the limit. |
| 429 | rate_limit_error |
Every configured upstream key for this user returned 429. The response preserves the upstream `Retry-After` header when one was provided. | Wait for the duration advertised in the `Retry-After` response header before retrying. |
| 502 | provider_error |
The upstream provider failed to return an HTTP response (network error, DNS failure, or connection reset) on every key attempt. | Retry the request after a short delay; transient upstream outages usually clear within seconds. |
| 402 | quota_exceeded |
The authenticated user has consumed their configured monthly budget (credits or tokens) and further calls are blocked until the next billing month. | Check the Usage tab to confirm the current month's spend and forecasted total. |
| 402 | account_suspended |
The Customer_Account that owns this sub-key has `status` of `suspended` or `closed`, so every request under any of its keys is rejected before being forwarded upstream. | Ask the admin to set the account `status` back to `active` via `scripts/account-update.js` once the account is in good standing. |
| 400 | account_not_found |
A sub-key creation request referenced a parent `account_id` that does not exist in the `accounts` table. | Confirm the `account_id` argument matches a row returned by `scripts/account-list.js`. |
| 400 | sub_key_limit_reached |
The parent Customer_Account already has the maximum of 50 enabled API sub-keys, so no additional sub-keys can be issued until one is revoked. | Revoke unused or stale sub-keys with `scripts/key-revoke.js` to free a slot under the 50-key cap. |
| 400 | balance_invariant |
An account write would have set `balance_usd` outside the inclusive range 0.00..1,000,000.00 or moved `total_spent_usd` backwards or above 999,999,999.99. The Account_Store rejected the write and the existing record is unchanged. | Verify the requested amount keeps `balance_usd` within 0.00..1,000,000.00 USD. |
| 402 | key_budget_exceeded |
The sub-key has a configured `budget_cap_usd` and the projected cost of this request would push `keys.spent_usd` over that cap. | Check the Dashboard usage panel to see how much of the per-key budget has already been spent. |
| 402 | key_token_cap_exceeded |
The sub-key has a configured `token_cap` and the projected input plus output tokens for this request would push `keys.tokens_used` over that cap. | Trim the prompt or reduce the requested completion length so projected tokens fit under the remaining cap. |
| 502 | ledger_write_failed |
The atomic D1 transaction that records a usage ledger row and updates the account and key counters failed to commit. No partial mutation of `accounts.balance_usd`, `accounts.total_spent_usd`, `keys.spent_usd`, or `keys.tokens_used` is observable. | Retry the request after a short delay; transient D1 commit errors usually clear within seconds. |
| 409 | ledger_retention_violation |
An attempt was made to delete a ledger row before the 365-day retention window since `created_at` has elapsed. The ledger is append-only within the retention period and the row remains in place. | Wait until the row's `created_at` is more than 365 days old before retrying the delete. |
| 410 | key_show_expired |
The 5-minute Show again window for re-displaying a freshly-minted API sub-key has elapsed, the show-again token is unknown, or the token is bound to a different account or key. The raw key is no longer recoverable from the dashboard. | Revoke the current key and mint a replacement so a fresh show-again token is issued. |
| 409 | invalid_state |
The targeted resource is not in a state that allows the requested operation. For example, hard-deleting a sub-key requires the key to already be revoked; calling the hard-delete endpoint on an `enabled` or `exhausted` key surfaces this error and leaves every related row (`keys`, `key_allocations`, `accounts.balance_usd`, `ledger`) unchanged. | Revoke the sub-key first via DELETE /dashboard/api/keys/:id?confirm=true, then retry the permanent-delete request once status is `revoked`. |
| 409 | conflict |
The requested state transition is not legal for this resource. For example, calling POST /dashboard/api/keys/:id/enable on a key with status `revoked` or `exhausted` is rejected with this error: revoked keys cannot be re-enabled (delete the row and mint a fresh key instead) and exhausted keys regain capacity by other means, not by toggling the enable flag. | If the key is `revoked`, hard-delete it via DELETE /dashboard/api/keys/:id?confirm=true&permanent=true and mint a new key with the desired allocation. |
| 500 | internal_error |
The Worker hit an unexpected condition (typically a scrub-layer failure) and replaced the upstream body with a generic error response so no unscrubbed content is returned to the client. | Retry the request after a short delay; the condition is usually transient. |