# Error-code reference

Every error response carries a stable `error.code`. Search or filter the table, then open a code for its causes, fixes, retry guidance and an example response. The envelope itself is described in [Error handling](/fundamentals/error-handling).

| Error code | HTTP status | Meaning | How to fix | Retry? |
| --- | --- | --- | --- | --- |
| [`invalid_cursor`](/errors/invalid_cursor) | 400 | The pagination cursor is invalid or does not match these filters. | Send `next_cursor` exactly as returned, with the same filters. To change filters, start again without a cursor. | No |
| [`invalid_idempotency_key`](/errors/invalid_idempotency_key) | 400 | The Idempotency-Key header must be 1 to 255 printable ASCII characters with no spaces. | Use a UUID v4 or another URL-safe token of at most 255 characters. | No |
| [`invalid_json`](/errors/invalid_json) | 400 | The request body is not valid JSON. | Serialize the body with a JSON library rather than by string concatenation. | No |
| [`invalid_phone_number`](/errors/invalid_phone_number) | 400 | The phone number is not a valid E.164 number. | Normalize numbers to E.164 before sending them. | No |
| [`validation_error`](/errors/validation_error) | 400 | One or more fields are invalid. | Read `details.errors[].field` and `details.errors[].issue`, correct each field and resend. | No |
| [`api_key_expired`](/errors/api_key_expired) | 401 | This API key has expired. | Switch to the replacement key, or ask a workspace admin to create a new key in the console. | No |
| [`api_key_revoked`](/errors/api_key_revoked) | 401 | This API key has been revoked. | Ask a workspace admin for a new key. | No |
| [`authentication_required`](/errors/authentication_required) | 401 | Send your API key as a Bearer token in the Authorization header. | Send `Authorization: Bearer rai_test_...` (test mode) or `Authorization: Bearer rai_live_...` (live mode). | No |
| [`invalid_api_key`](/errors/invalid_api_key) | 401 | The API key is not valid. | Copy the key again from where you stored it when it was created. Keys are shown only once; if it is lost, create a new key in the console under API & Developers. | No |
| [`legacy_api_key_not_supported`](/errors/legacy_api_key_not_supported) | 401 | Legacy API keys cannot be used with the v1 API. Create a scoped key in the console. | Create a scoped key in the console under API & Developers and use it instead. | No |
| [`campaign_budget_reached`](/errors/campaign_budget_reached) | 402 | This campaign has reached its budget cap. | Raise or clear `budget_cap` with PATCH /v1/campaigns/{campaign_id}, then start or resume. | No |
| [`insufficient_balance`](/errors/insufficient_balance) | 402 | The workspace balance is too low to start this call. Top up to continue. | A workspace admin tops up the balance in the console (Billing), then retry. | No |
| [`spend_limit_reached`](/errors/spend_limit_reached) | 402 | This workspace has reached the monthly spend limit set by its admin. | A workspace admin raises or removes the limit in the console settings. | No |
| [`account_inactive`](/errors/account_inactive) | 403 | This workspace cannot place calls because its account is not active. | A workspace admin can upgrade the plan in the console, or contact support about a suspension. | No |
| [`api_access_not_enabled`](/errors/api_access_not_enabled) | 403 | API access is not enabled for this workspace. | Write to support@relationsai.com to request access for the workspace. | No |
| [`insufficient_permissions`](/errors/insufficient_permissions) | 403 | The member who created this API key no longer has permission for this operation. | Ask a workspace admin to create a new key from an account that holds the permission. | No |
| [`insufficient_scope`](/errors/insufficient_scope) | 403 | This API key does not have the scope this operation requires. | Ask a workspace admin to create a key that includes the scopes in `details.required_scopes`. | No |
| [`ip_not_allowed`](/errors/ip_not_allowed) | 403 | Requests from this IP address are not allowed for this workspace. | Ask a workspace admin to add the server's egress address to the allowlist in the console security settings. | No |
| [`live_key_not_allowed_in_browser`](/errors/live_key_not_allowed_in_browser) | 403 | Live API keys cannot be used from a browser. Use a test key, or call the API from your server. | Move live calls to your server. | No |
| [`webhooks_not_enabled`](/errors/webhooks_not_enabled) | 403 | Webhook delivery is not available yet. | Write to support@relationsai.com to ask when webhook delivery will be available. | No |
| [`resource_not_found`](/errors/resource_not_found) | 404 | No such resource. | Check the id and the key mode. List the resource to find valid ids. | No |
| [`route_not_found`](/errors/route_not_found) | 404 | No such route. | Compare the path with the API reference. | No |
| [`method_not_allowed`](/errors/method_not_allowed) | 405 | This route does not support that HTTP method. | Use a method listed in the `Allow` header. | No |
| [`campaign_dnc_override_enabled`](/errors/campaign_dnc_override_enabled) | 409 | This campaign is set in the console to also call contacts on the do-not-call list, so it cannot be started or resumed through the API. | Ask a workspace admin to switch the setting off in the console, then start or resume it through the API. | No |
| [`campaign_has_no_recipients`](/errors/campaign_has_no_recipients) | 409 | This campaign has no recipients to call. | Add recipients with POST /v1/campaigns/{campaign_id}/recipients, then start it. | No |
| [`campaign_not_resumable`](/errors/campaign_not_resumable) | 409 | This campaign cannot be resumed. Start it again to begin a new run. | To call only the people not reached yet, list the recipients whose status is still `pending`, create a new campaign with those contacts, and start it. | No |
| [`campaign_operation_in_progress`](/errors/campaign_operation_in_progress) | 409 | This campaign is being started or resumed by another request. Retry shortly. | Wait `Retry-After` seconds, retrieve the campaign, and act on its new status. | After Retry-After |
| [`campaign_outcome_unknown`](/errors/campaign_outcome_unknown) | 409 | The request was sent but its outcome is not confirmed yet. Check the campaign before trying again. | Wait `Retry-After` seconds, then retrieve the campaign: a `running` status means it started. Do not start or resume it again before you have checked. | After Retry-After |
| [`campaign_pause_not_confirmed`](/errors/campaign_pause_not_confirmed) | 409 | The pause was requested but not confirmed. The campaign may still be dialling. | Retry the pause (with the same Idempotency-Key if you sent one; a repeated pause is harmless), then retrieve the campaign. If it is still running, use stop. | Yes, with backoff |
| [`campaign_recipients_blocked`](/errors/campaign_recipients_blocked) | 409 | Some recipients of this campaign are now blocked by compliance rules, so it was not resumed. | List the campaign's recipients with `status=pending` (the people not reached yet), create a new campaign with those contacts minus the blocked ones in `details.contact_ids`, and start it. | No |
| [`campaign_state_conflict`](/errors/campaign_state_conflict) | 409 | This action is not allowed in the campaign's current status. | Retrieve the campaign, check `status` and `capabilities`, and choose an allowed action. | No |
| [`contact_already_exists`](/errors/contact_already_exists) | 409 | A contact with this phone number already exists. | Update the existing contact, or use the upsert operation. | No |
| [`contact_limit_reached`](/errors/contact_limit_reached) | 409 | No more contacts can be created right now. | Delete contacts you no longer need, or contact support. | No |
| [`contact_on_dnc_list`](/errors/contact_on_dnc_list) | 409 | This contact is on the do-not-call list. Remove its do-not-call entry before deleting it. | Keep the contact: it is what keeps the number from being called. Do not retry: the same request is refused every time. | No |
| [`idempotency_key_in_progress`](/errors/idempotency_key_in_progress) | 409 | A request with this Idempotency-Key is still being processed. | Wait for `Retry-After` seconds and send the identical request again with the same key. | After Retry-After |
| [`idempotency_key_reused`](/errors/idempotency_key_reused) | 409 | This Idempotency-Key was already used with a different request. | Use a new key for a new operation. For a retry, send exactly the original request. | No |
| [`idempotency_outcome_unknown`](/errors/idempotency_outcome_unknown) | 409 | An earlier request with this Idempotency-Key did not finish, and its outcome is unknown. | List or retrieve the resource (for a call, list calls filtered by `to` and `created_after`) to see whether the operation took effect. | No |
| [`webhook_delivery_not_retryable`](/errors/webhook_delivery_not_retryable) | 409 | This delivery cannot be retried in its current status. | Retrieve the delivery list and retry only `failed` or `retrying` deliveries; for `attempt_in_progress`, wait a few seconds and read the delivery again. | No |
| [`webhook_endpoint_disabled`](/errors/webhook_endpoint_disabled) | 409 | This webhook endpoint is disabled. | Enable it with PATCH /v1/webhook-endpoints/{webhook_endpoint_id} and `status: enabled`. | No |
| [`webhook_endpoint_limit_reached`](/errors/webhook_endpoint_limit_reached) | 409 | This workspace already has the maximum number of webhook endpoints for this mode. | Delete an endpoint you no longer use, or subscribe one endpoint to more event types. | No |
| [`media_link_expired`](/errors/media_link_expired) | 410 | This recording link has expired. Retrieve the recording again for a new link. | Call GET /v1/calls/{call_id}/recording again and use the new `url` promptly. | No |
| [`version_conflict`](/errors/version_conflict) | 412 | The resource changed since you read it. | Retrieve the resource again, re-apply your change to the new version, and resend with the new `ETag`. | No |
| [`payload_too_large`](/errors/payload_too_large) | 413 | The request body is too large. | Split the request into smaller batches. | No |
| [`unsupported_media_type`](/errors/unsupported_media_type) | 415 | Request bodies must be sent as application/json. | Send `Content-Type: application/json` with a JSON body. | No |
| [`agent_not_ready`](/errors/agent_not_ready) | 422 | This agent cannot place calls right now. | Call GET /v1/agents/{agent_id}/readiness to see every reason. | No |
| [`call_not_accepted`](/errors/call_not_accepted) | 422 | The call could not be placed. Nothing was dialled and nothing was charged. | Check the destination number and the agent's outbound number. If it keeps happening, contact support with the request_id. | No |
| [`calling_not_configured`](/errors/calling_not_configured) | 422 | Calling is not set up for this workspace yet. | Finish calling setup in the console (Numbers), then retry. | No |
| [`campaign_recipients_suppressed`](/errors/campaign_recipients_suppressed) | 422 | Every recipient of this campaign is blocked by your compliance settings. Nothing was dialled. | This is a compliance outcome: do not retry; an unchanged start is refused again. | No |
| [`campaign_too_large`](/errors/campaign_too_large) | 422 | This campaign has too many recipients to start in one run. | Start: split recipients across several campaigns. | No |
| [`consent_required`](/errors/consent_required) | 422 | This contact has no recorded consent, and consent is required for calls in this workspace. | Do not retry: the same request is refused every time until consent is recorded. | No |
| [`contact_daily_limit_reached`](/errors/contact_daily_limit_reached) | 422 | This number has already been called the maximum number of times allowed in 24 hours. | Do not retry now: the same request is refused until the oldest of those calls is more than 24 hours old. | No |
| [`custom_field_not_found`](/errors/custom_field_not_found) | 422 | This custom field does not exist in the workspace. | Create the field in the console, or correct the key named in `details.field`. | No |
| [`dnc_listed`](/errors/dnc_listed) | 422 | This number is on your do-not-call list, so the call was not placed. | Do not call this number, and do not retry: the same request is refused every time. | No |
| [`outside_calling_hours`](/errors/outside_calling_hours) | 422 | This is outside the permitted calling window for this workspace. Nothing was dialled. | Do not retry now: the same request is refused until the window opens. Send it again inside the window in `details` (a new request; the refusal was not stored). | No |
| [`phone_number_not_usable`](/errors/phone_number_not_usable) | 422 | This phone number cannot place outbound calls for this agent. | Pick a number whose `capabilities.outbound` is true, or omit `phone_number_id` to use the agent's outbound number. | No |
| [`phone_number_required`](/errors/phone_number_required) | 422 | This agent has more than one outbound number. Choose one with phone_number_id. | Send `phone_number_id`, or assign the agent to a single outbound number in the console. | No |
| [`recipient_contacts_not_found`](/errors/recipient_contacts_not_found) | 422 | Some contact ids do not name contacts in this workspace. | Create the contacts first (POST /v1/contacts/upsert), then add their ids. | No |
| [`telephony_disconnected`](/errors/telephony_disconnected) | 422 | The telephony connection for this workspace is not working, so the campaign was paused. | Reconnect the telephony account in the console, then start or resume the campaign again. | No |
| [`webhook_url_not_allowed`](/errors/webhook_url_not_allowed) | 422 | This URL cannot receive webhooks. | Use a public HTTPS URL on port 443 that resolves only to public addresses. Authenticate deliveries by verifying the signature instead of putting credentials in the URL. | No |
| [`precondition_required`](/errors/precondition_required) | 428 | This update needs an If-Match header with the ETag of the version you read. | Retrieve the resource, copy its `ETag` response header into `If-Match`, and resend. | No |
| [`calling_capacity_reached`](/errors/calling_capacity_reached) | 429 | Too many calls are being started right now. Retry after the number of seconds in the Retry-After header. | Wait `Retry-After` seconds and retry (with the same Idempotency-Key if you sent one). Spread call starts out, or use a campaign for volume. | After Retry-After |
| [`daily_call_limit_reached`](/errors/daily_call_limit_reached) | 429 | This workspace has reached its daily limit of calls placed through the API. | Wait until `Retry-After` (the next UTC midnight). | After Retry-After |
| [`rate_limited`](/errors/rate_limited) | 429 | Too many requests. Retry after the number of seconds in the Retry-After header. | Wait `Retry-After` seconds. Spread requests out and back off exponentially. | After Retry-After |
| [`internal_error`](/errors/internal_error) | 500 | Something went wrong on our side. | For a read: retry with exponential backoff. | Yes, with backoff |
| [`campaign_recipients_unavailable`](/errors/campaign_recipients_unavailable) | 503 | The campaign's recipients could not be read for screening right now, so nothing was dialled. | Wait `Retry-After` seconds and retry (with the same Idempotency-Key if you sent one). | After Retry-After |
| [`service_unavailable`](/errors/service_unavailable) | 503 | The service is temporarily unavailable. Retry after the number of seconds in the Retry-After header. | Wait `Retry-After` seconds and retry (with the same Idempotency-Key if you sent one). | After Retry-After |
