MCP server docs / Limits and errors
Limits and errors
The Queue CallBack MCP server reports a problem in one of two ways: an HTTP error before any tool runs, with a stable code in error.data.code, or a tool result with isError: true and a stable code in structuredContent.error.code. This page lists the limits, every code with its meaning and its fix, and the rules for when an agent may retry.
For AI agents: this page is also available as Markdown at
https://queuecallback.com/docs/mcp/errors.md. The whole MCP documentation in one file: https://queuecallback.com/docs/llms-full.txt
Every code on this page has its own anchor, https://queuecallback.com/docs/mcp/errors#<code>, for example #mcp_disabled. Branch on the code, not on the message: messages are written for people and can change.
Limits
These are the limits of the Queue CallBack MCP server and of the Queue CallBack API behind it. The values are the current defaults, and Queue CallBack can change them. A Retry-After header or a retry_after_seconds field in an answer always wins over this table.
| Limit | Value | Answer when exceeded |
|---|---|---|
| Request body | 1 MiB per HTTP request | HTTP 413, not a JSON-RPC body |
| Requests in flight per credential | 8 at once for one API key, or for one person signed in from Claude or ChatGPT; the server also caps its total | HTTP 429 concurrency_limited, Retry-After: 1 |
| API requests in flight per organization | 4 at once, counting every API key and connected client of the organization; a total cap also applies across the service | 429 concurrency_limited, retry after 1 second |
| API request rate per key | 120 requests per minute, bursts of 30, for one API key or one signed-in person. One tool call makes one or more API requests | 429 rate_limited with Retry-After |
| API request rate per organization | 300 requests per minute for all of its keys and signed-in people together, bursts of 60 | 429 rate_limited with Retry-After |
| Requests per client address | a per-address request limit at the edge | HTTP 429 rate_limited as a JSON-RPC error, Retry-After: 1 |
| Tool time limit | 30 seconds; the reporting tools 40 seconds; edit_flow_draft, publish_flow and restore_flow_version 60 seconds |
backend_timeout (edit_interrupted for edit_flow_draft). The change may still complete |
| Result size | 100,000 bytes of JSON; validate_flow 200,000; get_flow, diff_flow and edit_flow_draft 300,000 |
A long list is halved until it fits and marked truncated; otherwise result_too_large |
| Items per list | each list tool has a default and a maximum limit (tool reference) |
The result carries truncated: true and a truncation object with pointer, returned, total and note |
| Report date range | 400 days from from to to per request |
date_range_too_long |
One edit_flow_draft call |
100 operations and 1 MiB together; 64 KiB per node config_json; node id 100 characters, name 255, condition 200, label 255 |
tool_refused, before anything is changed |
| Queue changes | per organization, through API keys and connected clients: 3 at once, then 1 per minute, counted separately for callback settings changes and for voice and language changes. A settings call that changes nothing is not counted | 429 queue_write_limited with Retry-After |
Callbacks added with add_callback |
30 per rolling hour per organization, including callbacks added in the portal. Callbacks that callers request do not count | 429 manual_callback_limit_reached, with retry_after_seconds when the server can tell when a callback can be added again |
Requeues with requeue_callback |
5 per callback in 24 hours | 409 requeue_limit_reached |
Renames with set_organization_name |
about one every 6 seconds | 429 without a code (429 without a code) |
| Confirm token | single use, valid 10 minutes | confirm_refused |
| Handle for a long operation | 24 hours, only for the credential that received it | invalid_handle |
HTTP errors
An HTTP error from the Queue CallBack MCP server means the request was refused before any tool ran, so nothing changed. The exceptions are a 500, and a 502 or 504 from the edge, which can come while a tool runs. The server checks a request in this order: the Host name, the browser origin, the credential, the request limits, then the MCP transport rules. A request without a valid credential that passes the first two checks answers 401, even a GET or one with the wrong Content-Type.
Most HTTP errors carry a JSON-RPC error body with "id": null. Its error.code is -32001 for a credential problem (401 and 403) and -32000 for 429 and 5xx. error.data.code is the stable reason. error.data.retry_after_seconds is set, together with a Retry-After header, when waiting and retrying makes sense. Every answer from the server itself carries an X-Request-ID header; quote it when you contact Queue CallBack support. Answers made by the edge (the per-address 429, a 413, a 404 for an unknown path, a 502 or 504) and host_not_allowed have none.
This is the answer to a request without a valid API key or sign-in:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
WWW-Authenticate: Bearer resource_metadata="https://mcp.queuecallback.com/.well-known/oauth-protected-resource/mcp", error="invalid_token", scope="ivr:read reports:read callbacks:read queues:read org:read"
X-Content-Type-Options: nosniff
X-Request-ID: <request id>
{"error":{"code":-32001,"data":{"code":"invalid_token"},"message":"The credential is missing, malformed or unknown, or the API key has expired, been revoked or been suspended (a key is suspended while the admin who created it is deactivated or no longer in the organization), or the sign-in has ended. Send a valid Queue CallBack API key as Authorization: Bearer \u003ckey\u003e, or sign in again; an organization admin sees each key's status on the portal's API keys page."},"id":null,"jsonrpc":"2.0"}
\u003c and \u003e are JSON escapes for < and >. The message lists every state that gets this answer, because the server cannot tell them apart. The WWW-Authenticate header tells a client where to sign in: resource_metadata points to the discovery document, and scope names the five read scopes a sign-in asks for. Claude and ChatGPT use it to start the sign-in (OAuth sign-in). A client that sends an API key does not need it.
| HTTP status | JSON-RPC error.code |
error.data.code |
Meaning | Fix |
|---|---|---|---|---|
| 400 | usually none (plain text) | none | Accept does not list both application/json and text/event-stream, the body is not valid JSON-RPC, or the MCP-Protocol-Version header names a revision the server does not support. |
Send Accept: application/json, text/event-stream and a supported protocol revision. |
| 401 | -32001 |
invalid_token |
For an API key: the key is missing, malformed, expired, revoked or suspended; Queue CallBack has switched API keys off for the whole service; or more than one Authorization header was sent. For a sign-in from Claude or ChatGPT: the sign-in expired or was not accepted, or the signed-in account is not an active user of a Queue CallBack organization. A bearer that is neither a Queue CallBack API key nor an accepted sign-in token gets the same answer. Apart from the case of two Authorization headers ("Send exactly one Authorization header."), the message is the same for every cause, so it does not say which check failed. A client that signs in starts a new sign-in on this answer. |
API key: check the key's status in the portal (Settings, then Manage API keys), check that QCB_API_KEY is set where the client runs, and send exactly one Authorization: Bearer <key> header. Sign-in: sign in again with your Queue CallBack portal account; if the client keeps asking, see sign-in fails or repeats. |
| 403 | -32001 |
mcp_disabled |
The organization's API access is off: "API and MCP access is not enabled for this organization". Every request is refused, with a valid key and after a successful sign-in alike. A tool result carries the same code when access goes off during a session; within about 30 seconds every request gets this HTTP 403. | Nothing in the key or the client changes this answer. If you see it, contact Queue CallBack support (support@queuecallback.com) with the X-Request-ID. |
| 403 | -32001 |
insufficient_scope |
Sign-in only: the sign-in token grants none of the five read scopes the server asks for. WWW-Authenticate carries error="insufficient_scope" and scope with those scopes, so the client can sign in again for them. An API key never gets this answer before a tool runs. |
Remove the connection in the client, add it again, sign in and approve the access (sign-in fails or repeats). |
| 403 | -32001 |
no_organization |
Sign-in only: the signed-in account belongs to no Queue CallBack organization. | Sign in with the account of your organization, or ask an organization admin to add you. |
| 403 | -32001 |
forbidden |
The Queue CallBack API refused the credential for another reason; the message says why. | Follow the message, or contact Queue CallBack support with the X-Request-ID. |
| 403 | none (plain JSON) | cross_origin_refused, in the body's error field |
A web page on another site sent the request from a browser. The server sends no CORS headers on /mcp. |
Call the server from an MCP client or from server-side code, not from a web page. |
| 403 | none (plain JSON) | host_not_allowed, in the body's error field |
The request's Host header is not the server's name. |
Use https://mcp.queuecallback.com/mcp. |
| 404 | none | none | The path is not /mcp, /healthz or the discovery document. |
Send MCP requests to /mcp. |
| 405 | none (plain text) | none | A GET or DELETE with a valid key. The server is stateless and offers no event stream. The answer carries Allow: POST. |
Send every request as a POST. |
| 413 | none | none | The request body is larger than 1 MiB. | Send less. Split a large edit_flow_draft into several calls. |
| 415 | none (plain text) | none | Content-Type is not application/json. |
Send Content-Type: application/json. |
| 429 | -32000 |
concurrency_limited |
More than 8 requests in flight for this credential, the server at its total, or the organization at its API cap while the credential was checked. Retry-After: 1. |
Wait, then send fewer requests in parallel. |
| 429 | -32000 |
rate_limited |
The key or the organization exceeded its request rate while the credential was checked. | Wait for Retry-After. |
| 429 | -32000 |
rate_limited |
The per-address request limit at the edge in front of the server. The message reads "Too many requests from this address. Retry in a second." Retry-After: 1 and error.data.retry_after_seconds 1; no X-Request-ID. |
Wait at least 1 second, then send requests more slowly (Retries). |
| 500 | none (plain JSON) | internal_error, in the body's error field |
The server failed while handling the request. | Read the current state before you repeat a change. Contact Queue CallBack support with the X-Request-ID if it repeats. |
| 502 | -32000 |
backend_unreachable |
The credential could not be checked because the Queue CallBack API could not be reached. Retry-After: 5. |
Retry after Retry-After. |
| 502 | -32000 |
backend_error |
The credential could not be checked because the Queue CallBack API answered in an unexpected way. | Retry after Retry-After, or after a few seconds when it is missing. If it repeats, contact Queue CallBack support with the X-Request-ID. |
| 502, 504 | none | none | The edge in front of the server answered itself, because the server is restarting or did not answer in time. The body is not JSON-RPC and there is no X-Request-ID. If the request was a tool call, the change may have run. |
Repeat a read after a few seconds. For a change, read the current state first (Retries). |
| 503 | -32000 |
auth_busy |
Too many credential checks are running. A tool result can carry the same code, with status 503. Retry-After: 1. |
Retry after 1 second. |
| 503 | -32000 |
backend_unavailable |
The Queue CallBack API is busy. | Retry after Retry-After. |
| 503 | -32000 |
feature_unavailable |
The Queue CallBack API cannot check API keys right now because of a fault on the service. The message reads "API keys and delegated credentials are not enabled on this Queue CallBack server." Retry-After is usually 5 seconds. |
Retry after Retry-After. If it repeats, contact Queue CallBack support with the X-Request-ID. |
| 503 | -32000 |
authorization_server_unavailable |
Sign-in only: the sign-in service's signing keys could not be read, so the sign-in token could not be checked. The token may be valid. Retry-After: 5. |
Keep the token and retry after the wait. Do not sign in again. |
| 503 | -32000 |
canceled |
The request was canceled while it waited, usually because the client gave up. | Send it again if you still need the answer. |
| 200 (400 with protocol revision 2026-07-28) | -32602 |
none | The message reads unknown tool "<name>": the name is not a tool of the Queue CallBack MCP server at all, for example a typo or a tool of another server. A tool of the server that this credential does not see (a missing scope, the Custom IVR add-on, the IVR call reports, a server switch) is not a protocol error: the call is refused as a tool result with a code that says why (Access, scopes and switches). |
Fetch the tool list again and use a name from it. Check the connection with get_me (check the connection). |
Tool errors
A tool error is a normal JSON-RPC result from the Queue CallBack MCP server, not a protocol error. isError is true, the text content says what happened and how to fix it, and structuredContent.error carries the details. The tool did not do what was asked. Whether anything changed depends on the code: see Retries.
The error object
This is a tool error the Queue CallBack API raised (a rate limit):
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"isError": true,
"content": [
{
"type": "text",
"text": "HTTP 429 (rate_limited): Too many requests for this credential or organization; retry after the time in Retry-After. Too many requests; retry after 3 seconds."
}
],
"structuredContent": {
"error": {
"status": 429,
"code": "rate_limited",
"message": "Too many requests for this credential or organization; retry after the time in Retry-After",
"retry_after_seconds": 3,
"details": {
"error": "Too many requests",
"code": "rate_limited",
"message": "Too many requests for this credential or organization; retry after the time in Retry-After",
"retry_after_seconds": 3
}
}
}
}
}
Field of structuredContent.error |
Present | Holds |
|---|---|---|
code |
almost always; missing only when the API refused without a code (answers without a code) | The stable reason. Branch on it |
message |
always | The reason in plain words |
status |
most API answers | The API's HTTP status, or a 502 or 504 that the server sets itself for edit_interrupted and some backend_error answers. Without status, the MCP server answered on its own: either it refused the call before any change (refused, request_refused, tool_refused, confirm_required, confirm_refused, invalid_handle, and the codes for a tool this credential does not see, such as insufficient_scope or writes_disabled), or the API's answer was missing, late or unusable (backend_timeout, backend_unreachable, backend_error, result_too_large, no_answer), and then a change may have happened |
required_scope |
with insufficient_scope |
The scope the call needs |
retry_after_seconds |
when the API sent Retry-After |
Seconds to wait before a retry |
details |
API refusals with a 4xx status | The API's whole answer, for example a current etag, failed_ops, a validation report, a callback_id or a retry_after time |
issued_by |
with confirm_required |
The tool that issues the missing token |
confirm_token_spent |
true when the call's confirm token is used up: on any failed call of a tool that took a confirm token, and on confirm_refused for a token an earlier call used |
The token cannot be sent again. Get a new one before you retry (Confirm tokens are spent on use) |
next_step |
with confirm_token_spent on a failed call |
How to retry: where the new token comes from and, for requeue_callback, which idempotency_key to send |
For an API refusal the text reads HTTP <status> (<code>): <message>. <hint>. For a refusal by the MCP server the text is the message. Read structuredContent.error when it is present; otherwise read the code from the text.
Access, scopes and switches
| Code | status |
Meaning | Fix |
|---|---|---|---|
insufficient_scope |
403, or none | The credential lacks the scope in required_scope. With status 403 the Queue CallBack API refused the call. Without status, the MCP server refused a call to a tool that this credential does not see, before anything was sent, with a message such as "This credential lacks the callbacks:dial scope that requeue_callback needs. The API key was created without it, the organization or the role the credential acts with does not allow it, or this is a sign-in, which can only read. get_me lists the scopes this credential has and its role. For more, an organization admin can create an API key with that scope; signing in again does not add scopes." required_scope can then be a scope the key holds that does not take effect in the organization, for example ivr:write where IVR flows are not edited through drafts, or pii:read for add_callback where caller data stays masked. Without an effective pii:read, it is also the answer to a search made only of digits and phone punctuation, and to the caller filter or sort=caller; a sign-in from Claude or ChatGPT never has pii:read, so it always gets this answer for those. A signed-in connection lists no tool that changes something, and a call to one by name gets this code. A User's sign-in has no ivr:read, so the IVR flow tools answer this code too. Before any tool runs, HTTP 403 with this code means a sign-in token grants none of the five read scopes (HTTP errors). |
Use an API key that has the scope (scopes); a sign-in cannot gain more scopes. If the key holds the scope but get_me does not list it, an organization setting keeps it from taking effect (When a scope does not take effect). For masked numbers, see caller data. |
mcp_disabled |
403 | The organization's API access went off during the session. | See mcp_disabled under HTTP errors. |
invalid_credentials |
401 | The API refused the credential during a session: the key was revoked, expired or suspended moments ago, API keys were switched off for the whole service, or the signed-in person was deactivated or left the organization. Within about 30 seconds, requests get HTTP 401 invalid_token. |
Replace the key (rotate, revoke and suspension) and restart the client. For a sign-in, sign in again with an active account. |
route_not_allowed |
403 | The operation is not available to API keys. | Use the Queue CallBack portal. |
custom_ivr_not_enabled |
403, or none | The organization does not have the Custom IVR add-on (the portal's Billing page sells it as the Custom IVA plan), so the IVR flow tools are unavailable. Without status, the MCP server refused a call to an IVR flow tool that is not listed for the organization. The queue, callback and callback report tools still work. |
If the message names the Billing page, an organization admin can buy or restore the add-on there. Otherwise ask Queue CallBack support. |
ivr_reporting_not_enabled |
403, or none | The IVR call reports are for organization admins of organizations with the Custom IVR add-on (or of organizations that keep IVR reporting after the add-on lapsed), and this credential is not one: get_me shows ivr_reporting_enabled: false. The five IVR report tools (get_ivr_summary, list_ivr_calls, get_ivr_call, get_flow_decisions, list_ivr_flow_stats) are then not listed. Without status, the MCP server refused the call before anything was sent; with 403, the Queue CallBack API refused it. The message ends: "The callback reports (get_callback_summary, list_callbacks, get_callback_journey, get_live_stats) still work." |
Use the callback reports, or ask an organization admin. An organization admin's API key or sign-in reads the IVR call reports where the organization has the add-on. |
custom_ivr_queue_required |
403 | The flow belongs to a queue that is not a Custom IVR queue, and the add-on covers only the flows of Custom IVR queues. Reading the flow still works. | An organization admin marks the queue as a Custom IVR queue in the portal (each Custom IVR queue uses one license of the Custom IVR add-on), then call again. |
writes_disabled |
none | Queue CallBack has switched off changes through the MCP server. While changes are off, the tools that change something are not listed, and a call to one by name, for example from an older tool list, gets this code and sends nothing. The message reads "Queue CallBack has switched off changes through this server for now. Make the change in the Queue CallBack portal; read-only tools still work." | Make the change in the portal, or try again later. |
queue_writes_disabled |
none | Queue CallBack has switched off changes to a queue's voice and language through the MCP server, apart from the other changes. While they are off, set_queue_voice and set_queue_language are not listed, and a call to either by name gets this code and sends nothing. |
Change them in the portal, or try again later. |
queue_settings_disabled |
none | Queue CallBack has switched off changes to a queue's callback settings through the MCP server, apart from the other changes. While they are off, update_queue_settings is not listed, and a call to it by name gets this code and sends nothing. |
Change them in the portal, or try again later. |
Arguments, confirm tokens and handles
| Code | status |
Meaning | Fix |
|---|---|---|---|
refused |
none | The arguments do not match the tool's input schema (an unknown argument, a wrong type, a missing required argument, a value outside its pattern), or are not one JSON object. The text names the argument, sometimes in raw schema-validator wording. Nothing was sent. | Fix the argument and call again. The tool reference lists every argument. |
request_refused |
none | The request could not be built or was stopped before sending, for example because an idempotency key is longer than 255 characters. Nothing was sent. If the message says the request "did not pass this server's own checks", the arguments are not the cause. | Fix the arguments and call again. When the arguments are not the cause, try again later, and contact Queue CallBack support if it repeats. |
tool_refused |
none | The tool's own check refused the call, and nothing was changed (the tool may have read the current state first). Examples: an edit_flow_draft call over its limits; an organization that edits its IVR flows live instead of through drafts; an unknown node type in get_node_type; get_live_stats with tz and timezone naming different zones. |
Follow the message. |
confirm_required |
none | publish_flow was called with an empty confirm_token. A call that leaves the argument out is refused earlier with refused, because confirm_token is required. issued_by is validate_flow. |
Call validate_flow, show the user diff_flow, then call publish_flow with the digest, base version and token (confirm tokens). |
confirm_refused |
none | The confirm token is not valid, has expired, was already used, or was issued for another tool, credential, organization or other argument values. Rarely, the server holds too many open tokens. This call changed nothing. A token refused as invalid, expired or issued for other values is not spent. A token that was already used comes back with confirm_token_spent: true, and the message says that the earlier call that used it may have made the change: a call that timed out or got no answer can still have run. |
With confirm_token_spent: true, read the current state first (get_queue, get_requeue_status or list_callback_queue, list_flow_versions). Then get a new token from the issuing call (validate_flow for publish_flow; the same tool without confirm_token for the others), show the user what will change, and call again; for requeue_callback, send the same idempotency_key as the earlier call. Never invent or reuse a token. |
invalid_handle |
none | The handle is unknown, expired (handles last 24 hours) or belongs to another credential. The message says how to check whether the operation already ran, by the kind of handle. | For get_requeue_status: do not requeue again yet. Check the callback in list_callback_queue, and requeue it only if it is still expired, failed or cancelled, with the same idempotency_key as before. For get_publish_status: the publish or restore already happened, so do not publish again; list_flow_versions shows the current version. Only the warm-up report is lost. |
idempotency_key_reused |
422 | The idempotency key was already used in this organization for a different request (another queue, number or callback). Nothing was done. | Send a new key. Reuse a key only to repeat the identical request. |
invalid_idempotency_key |
400 | The API refused the key's format. Through the MCP server this is rare: a key with characters other than letters, digits, ., _, : and - is refused first with refused, and a key longer than 255 characters with request_refused. Nothing was done. |
Use letters, digits, ., _, : and -, at most 255 characters. |
idempotency_key_in_progress |
409 | A requeue with this idempotency key is still running. | Wait, then check get_requeue_status or list_callback_queue. Keep the same key. |
Limits and availability
| Code | status |
Meaning | Fix |
|---|---|---|---|
rate_limited |
429 | The API key or the organization sent requests faster than its rate (Limits). Can also arrive as HTTP 429 before any tool runs, from the credential check or from the edge's per-address limit (HTTP errors). | Wait retry_after_seconds, then call again. |
concurrency_limited |
429 | More than 4 API requests were in flight at once for the organization, or the service was at its total for all organizations. Before any tool runs, HTTP 429 with this code can also mean more than 8 requests in flight for the credential at the MCP server. | Wait retry_after_seconds (1), and run fewer tool calls in parallel. |
queue_write_limited |
429 | The organization changed queue callback settings (or voices and languages) through API keys and connected clients too often. | Wait retry_after_seconds. |
auth_busy |
503 | Too many credential checks were running at the API. | Retry after retry_after_seconds (1). |
result_too_large |
none | The answer is larger than the tool's result limit, even after a long list was shortened. The API did answer, so for a tool that changes something the change may have happened. | Narrow the request: a shorter date range, filters or a smaller limit. After a change, read the current state before you repeat it. |
backend_timeout |
none | The Queue CallBack API did not answer within the tool's time limit. The operation may still complete. | A read: call again. A change: read the current state first (Retries). |
backend_unreachable |
none | The Queue CallBack API could not be reached. Can also arrive as HTTP 502 before any tool runs. | A read: retry shortly. A change: read the current state first. |
backend_error |
none, or 502 | The API's answer was unreadable or not in the documented shape. One case: edit_flow_draft was accepted but the answer lacked the draft's state, so the operations were most likely applied. |
Read the current state, then decide whether to repeat. |
no_answer |
none | The tool produced no answer. | Read the current state, then decide whether to repeat. |
Callbacks and reporting
| Code | status |
Meaning | Fix |
|---|---|---|---|
requeue_limit_reached |
409 | The callback was requeued 5 times in the last 24 hours, the most allowed. details.retry_after is the time it can be requeued again. |
Tell the user, and wait until that time. |
requeue_busy |
503 | Too many requeues are running on the service, so this one was not started. | Retry after retry_after_seconds, with a new confirm token (Retries). |
date_range_too_long |
400 | The report's range from from to to is longer than 400 days. |
Choose a shorter range. |
query_timeout |
504 | A report query took too long and was stopped. | Narrow the date range or the filters, then call again. |
reporting_busy |
503 | Too many report requests are running right now. | Retry after retry_after_seconds. |
interactive_busy |
503 | Too many quick reads are running right now. This can also answer list_flows, get_callback_journey and get_ivr_call. |
Retry after retry_after_seconds. |
invalid_timezone |
400 | get_live_stats got a time zone that is not an IANA time zone name. Nothing was read. The other reporting tools refuse an unknown tz with 400 and no code. |
Send an IANA name such as America/Chicago. |
add_callback refuses a callback with one of these codes. None of them adds a callback or places a call. For a 4xx answer, details carries the API's answer, whose texts are written for people.
| Code | status |
Meaning | Fix |
|---|---|---|---|
invalid_request |
400 | The number or the queue is missing. Through the MCP server this is rare: the tool's schema requires both, so a missing one is refused first with refused. |
Send callback_number and queue_id. |
invalid_callback_number |
400 | The number does not have 8 to 15 digits. | Ask the user for the whole number. Never use a masked number. |
callback_number_outside_country |
400 | The number is outside the organization's country; only numbers in that country can be called back. | Tell the user. A number with + must be in the organization's country. |
queue_not_found |
404 | No queue with this queue_id in the organization. Another organization's queue, and an id that is not a UUID, get the same answer. |
Take the id from list_queues. |
queue_in_other_organization |
403 | Only Queue CallBack's own administrators, who can name another organization, get this code. A key or sign-in of your organization gets queue_not_found for another organization's queue. |
Choose a queue of that organization. |
queue_not_running |
409 | The queue is stopped. | Tell the user to start the queue in the portal, then add the callback. |
queue_without_extension |
409 | The queue has no extension to ring. | Tell the user to set the queue's extension in the portal. |
callback_already_waiting |
409 | The number already has a callback waiting in that queue. details.callback_id names it. |
Do not add another. Use the waiting callback. |
manual_callback_limit_reached |
429 | The organization added 30 callbacks by hand in the last hour, in the portal or with add_callback. retry_after_seconds says when a callback can be added again; it is missing when the server cannot tell. |
Tell the user when they can add one again. Without a wait time, try again later: the limit counts a rolling hour. |
callback_queue_unavailable |
503 | The callback queue of the service is not available right now. | Retry later. Contact Queue CallBack support if it repeats. |
add_callback can also answer idempotency_key_reused, invalid_idempotency_key and the general codes on this page.
IVR flows (Preview)
These codes belong to the IVR flow tools, which are in Preview while the IVR builder is redesigned. Codes may be added or changed; the changelog records changes. edit_flow_draft can refuse an operation with a code that is not listed here; every such refusal names the operation in details.failed_ops, and nothing of the call is applied. The validation issues that validate_flow reports carry codes of their own, which are not listed here: branch on them and show the user the issue's text (Validate, review and publish). The editing loop is on Edit a flow safely.
| Code | status |
Meaning | Fix |
|---|---|---|---|
revision_mismatch |
409 | The draft changed since you read it, so your etag is stale. Nothing changed. details.etag is the current etag. |
Read the flow with get_flow, apply your change to the current draft, and send it with the current etag. |
node_exists |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. The node id is taken. |
Fix that operation and send the whole edit again with the same etag. |
node_not_found |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. No node has that id. |
Fix that operation and send the whole edit again with the same etag. |
invalid_body |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. The operation's fields or config_json are malformed. |
Fix that operation and send the whole edit again with the same etag. |
no_fields |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. An update_node changes nothing. |
Fix that operation and send the whole edit again with the same etag. |
entry_node |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. The entry node cannot be deleted. |
Fix that operation and send the whole edit again with the same etag. |
node_referenced |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. Other nodes still connect to the node. |
Fix that operation and send the whole edit again with the same etag. |
connection_target_missing |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. A connection points to a node that does not exist. |
Fix that operation and send the whole edit again with the same etag. |
invalid_node_type |
422 | edit_flow_draft was refused at the operation named in details.failed_ops; no operation of the call was applied. The node type is unknown. |
Get node types from list_node_types (node types), fix that operation and send the whole edit again with the same etag. |
edit_interrupted |
502 or 504 | edit_flow_draft got no answer, or none in time. An edit applies all of its operations or none. |
Read the flow with get_flow to see which happened before you send the edit again. |
no_draft |
409, or 404 from diff_flow |
The flow has no draft: there is nothing to publish, or nothing to compare. | To change the flow, start a draft with create_flow_draft, edit it, validate it, then publish. To compare versions only, call diff_flow with from and to set to published or version numbers. |
draft_exists |
409 | restore_flow_version with target live was refused because the flow has a draft and discard_draft is false. Nothing changed. |
Publish or discard the draft first, or, if the user agrees to lose the draft, start again with a first call that has discard_draft: true and get a new confirm token. |
draft_stale |
409 | The draft was started from an older version than the one now published. | Discard the draft (or restore into it) and make the change again. |
base_version_mismatch |
409 | base_version is not the flow's current version: someone published in the meantime. details.current_version is the current one. |
Read the flow, check the change with diff_flow, then run validate_flow again. |
digest_mismatch |
409 | The draft changed after validate_flow checked it. |
Run validate_flow again and publish with the new digest and token. |
validation_failed |
422 | The draft has errors; details.validation lists them. |
Fix them, or acknowledge the acknowledgeable ones, then validate again. |
version_not_found |
404 | No version of the flow has that number. | List the versions with list_flow_versions. |
draft_publish_disabled |
409 | The organization edits its IVR flows live, not through drafts, so the MCP server cannot change them. | Use the portal's flow builder, or ask Queue CallBack support to switch the organization to drafts. |
Answers without a code
Some refusals from the Queue CallBack API carry a status and a message but no code. Branch on status.
Retries
These rules tell an AI agent, or any client of the Queue CallBack MCP server, when to repeat a request, how long to wait, and what to read before repeating a change.
Wait as long as the server says
- When an answer has a
Retry-Afterheader or aretry_after_secondsfield (inerror.dataorstructuredContent.error), wait at least that long. - When a 429 or 503 comes without a wait time, back off exponentially with jitter: about 1, 2, 4, 8 and 16 seconds, never more than 60 seconds between attempts.
- Stop after about 5 attempts. Tell the user what failed and give the
code(and theX-Request-IDheader if the client shows it). - After
concurrency_limited, run fewer calls at once: at most 4 tool calls in parallel per organization, counting every key.
Repeat reads freely, changes only after reading the state
A read-only tool can be repeated after any error that is about load or availability: rate_limited, concurrency_limited, auth_busy, reporting_busy, interactive_busy, query_timeout (narrow the query first), backend_timeout, backend_unreachable, backend_error, HTTP 5xx.
A tool that changes something is different. After backend_timeout, backend_unreachable, backend_error, result_too_large, no_answer, edit_interrupted, a 5xx status, or a dropped connection, the change may have happened. Never repeat it blindly. Read the state first:
| Tool | Read this first | Then |
|---|---|---|
add_callback |
list_callback_queue: is a callback for that number waiting in that queue? |
Repeat with the same idempotency_key. The API replays the first answer instead of adding a second callback. |
requeue_callback |
get_requeue_status with the handle, if the first answer had one; otherwise list_callback_queue: is the callback back in the queue? |
Repeat only if it was not requeued (see below). |
complete_callback |
list_callback_queue |
A repeat is harmless: a callback already completed answers 404. |
update_queue_settings |
get_queue: compare the settings |
If they did not change, start again with the first call and the user's agreement. |
set_queue_voice, set_queue_language |
get_queue: compare the voice, speed or language |
If they did not change, start again with the first call and the user's agreement. |
set_organization_name |
get_organization |
Repeat if the name did not change. |
create_flow_draft, edit_flow_draft, discard_draft |
get_flow: the draft and its etag |
An edit applies all of its operations or none. Resend with the current etag only if they are missing. |
publish_flow, restore_flow_version |
list_flow_versions: is there a new version? With a handle, also get_publish_status. For restore_flow_version with target draft, which creates no version, read get_flow instead: does the draft now match the restored version (diff_flow from that version number to draft)? |
If nothing was published, run validate_flow again (for restore_flow_version, the first call again), then repeat with the user's agreement. |
An answer that refused the call before it ran (rate_limited, concurrency_limited, auth_busy, queue_write_limited, requeue_busy, any HTTP error other than a 500 or a 502 or 504 without a JSON-RPC body) changed nothing, and the change can be repeated after the wait.
Confirm tokens are spent on use
A confirm token is spent the moment the server accepts it, before the Queue CallBack API answers, even if the change then fails or is refused by a limit. Every repeat of a confirmed change therefore needs a new token: from validate_flow for publish_flow, and from a first call without confirm_token for requeue_callback, restore_flow_version, update_queue_settings, set_queue_voice and set_queue_language. Show the user what will change and get their agreement again.
A call that fails after its token was accepted says so. Its structuredContent.error carries confirm_token_spent: true and a next_step with the retry steps, and its text ends with the same steps:
| The failed call | next_step says |
|---|---|
publish_flow |
Call validate_flow again for a new token, then call publish_flow with it |
requeue_callback with an idempotency_key |
Call requeue_callback again without confirm_token for a new token, then call it again with that token and the same idempotency_key, so a requeue that already ran is not run twice |
requeue_callback without an idempotency_key |
Check the current state first, because the requeue may have run. Then get a new token the same way and send an idempotency_key with the retry |
| Any other tool with a confirm token | Call the tool again without confirm_token for a new token, then call it again with that token |
A confirm_refused answer changed nothing. With confirm_token_spent: true, the token was used by an earlier call, which may have made the change: read the current state before you get a new token. See confirm tokens.
Requeues place real calls
Every requeue_callback makes the system ring an agent and then call the customer. Never repeat a requeue without checking get_requeue_status first.
get_requeue_status status |
What it means | Next step |
|---|---|---|
pending, running |
The requeue is still in progress. | Check again in a few seconds. Do not requeue again. |
succeeded |
The callback is back in the queue. | Nothing to repeat. |
failed, conflict |
The callback was not requeued. | Read the answer and tell the user before trying again. A new attempt needs a new first call and a new idempotency_key. |
interrupted |
The server stopped before it recorded the outcome. | Check the callback with list_callback_queue before any new requeue. |
If get_requeue_status answers invalid_handle, check the callback with list_callback_queue instead of starting the requeue again. To learn what happened to the first attempt, call requeue_callback again with the same idempotency_key and a new confirm token: when the API recorded the first attempt, it replays the recorded request instead of dialing again. A first attempt that never reached the API runs now, so get the user's agreement for this call too. If the replayed status is failed or conflict and the user agrees to try again, use a new confirm token and a new idempotency_key: the old key only replays the failed attempt.
Idempotency keys
add_callback and requeue_callback take an idempotency_key argument: letters, digits, ., _, : and -, at most 255 characters. A client can send _meta.idempotencyKey on the request instead; the argument wins.
- Make one new random key, such as a UUID, for each action you intend.
- Send the same key with every repeat of that action. A repeat returns the recorded answer instead of acting twice, even when that answer was a failure: a requeue recorded as
failedorconflictneeds a new key to be tried again. - Never use the key for a different action. Keys are unique per organization, across all of its keys, and a reused key gets
idempotency_key_reused. - Send your own key with
add_callback. Without one, the server makes a key for that single call only, so a repeat call gets a different key.
For symptoms rather than codes (cannot connect, a tool is missing, numbers are masked), see troubleshooting.