Skip to main content
The API never answers a refusal with a bare status. Every one carries the same envelope:
code and message are always there. One of details, permission or retry_after_seconds is added when it applies.
  • code is one word from a closed list, for your program to branch on.
  • message is one sentence, for a person to read. It is the same sentence the console shows for the same refusal, so a marketer and an integrator looking at the same problem see the same words.
  • details appears on invalid_input: a map of field name to a list of messages when the workspace’s own validation refused, or of JSON pointer (/destination) to one message when the request failed schema validation.
  • permission appears on missing_permission: the permission the token lacks.
  • retry_after_seconds appears on rate_limited, and on the hourly limit_reached.

The codes

The list lives in the Error schema of the API reference, and that schema is generated from the API itself, so it is always current. In outline, the codes fall into four families:
  • The doorunauthenticated (401), subscription_required and subscription_ended (402), missing_permission and role_forbids (403), rate_limited (429). See Authentication & tokens and Rate limits.
  • The planplan_forbids (403) when the plan does not include the feature, limit_reached (429) when one of the two abuse bounds on the Rate limits page is hit, with Retry-After only for the hourly one. A report window the plan does not reach is invalid_input (422), below.
  • The rulesgovernance_refused when the workspace’s convention, approved values or dependency rules refuse the input, scanner_refused when the destination failed the safety scan, duplicate_link when the workspace already has a link with exactly that tagged destination (409, the only conflict in the set: the message names the existing link, and the right move is to use it rather than retry). The first two are 422.
  • The requestinvalid_input (422) with details, and not_found (404). A resource of another workspace and one that does not exist are the same answer, on purpose.

How the MCP server reports the same things

A tool call never returns this envelope. A refusal is a tool result flagged as an error, whose text is the message above, so the assistant can relay it or repair the input. A protocol-level error means the client spoke wrongly; a refusal means the product said no. The door’s refusals (401, 402, 429) do apply to the MCP server, and they fail the connection with that HTTP status rather than a tool result, because a token that is not admitted has no tools to list.