Errors
The API reports failures with a small, fixed set of error codes. Each code has one HTTP status and one message, so you can branch on error.code without parsing text.
On this page
The error envelope
{
"error": {
"code": "forbidden",
"message": "This credential may not perform this operation. The token may not include the scope it needs, or the account that issued it may not have access to this data, for example because that account is not yet linked to a worker or billing entity. A program admin can check the account's access."
},
"request_id": "…"
}error.codeis one of the six codes below.error.messageis fixed text for that code. It never contains details about your request, your workspace or any record.request_idmatches thex-request-idresponse header. Quote it when you contact support.retry_after_secondsappears only onrate_limited. See Rate limits and quotas.
Every error response carries cache-control: no-store.
Some problems are not errors. An operation that declines a request for a reason it names, such as an invalid cursor, returns 200 with data.status: "refused". See Named refusals are data.
Error codes
| Code | HTTP | Message |
|---|---|---|
unauthorized | 401 | The credential is missing, malformed, expired or revoked. |
not_enabled | 403 | Agent access is not available for this credential. |
forbidden | 403 | This credential may not perform this operation. The token may not include the scope it needs, or the account that issued it may not have access to this data, for example because that account is not yet linked to a worker or billing entity. A program admin can check the account's access. |
not_found | 404 (also 405 and 413) | No such operation, or the request did not match its arguments. |
rate_limited | 429 | The rate limit for this credential was exceeded. Retry later. |
internal_error | 500 | The operation could not be completed. Retry later. |
What causes each code
401 unauthorized
The Authorization header is missing or not in the form Bearer <token>, the token is malformed, expired or revoked, or the account that issued it has been deactivated. The response includes WWW-Authenticate: Bearer realm="agent-gateway". On a documented REST endpoint, VendOrca checks the token before the method and the arguments, so a request without a valid token gets 401 even if its method is also wrong. Only an oversized body is checked earlier; see 413.
403 not_enabled
The token is valid, but agent access is not available to its workspace right now. Agent access may be off or paused, a Program Admin may need to review an updated disclosure, or the workspace's plan may not include agent access. Existing tokens start working again once the workspace is back on, with no need to reissue them.
403 forbidden
The token is valid and agent access is on, but this call isn't allowed. The token may lack the scope the operation needs, the issuer's role may not be able to open the matching page in the app, or a Worker or Billing Entity sign-in may not yet be linked to its record, which the person's Program Admin arranges. See Scopes.
404 not_found
Over MCP, the tool name doesn't exist. Over REST and MCP, the arguments didn't match the operation, for example a value of the wrong type or out of range. Over REST, an unknown or repeated query parameter and a POST body that isn't valid JSON also get 404. Over MCP, a body that isn't valid JSON gets HTTP 400 with JSON-RPC error -32700 instead; see JSON-RPC errors. Read the operation's parameters on its resource page and fix the request.
405 with Allow
The path exists but not for this method, for example a POST to /api/v1/workers. The status is 405, the body code is not_found, and the Allow header lists the method to use:
HTTP/1.1 405 Method Not Allowed
allow: GET
content-type: application/json; charset=utf-8
cache-control: no-store
x-request-id: …
{"error":{"code":"not_found","message":"No such operation, or the request did not match its arguments."},"request_id":"…"}413 payload too large
The request body is larger than 64 KiB. The body code is not_found. Real requests are a few hundred bytes, so a 413 usually means the wrong data was sent.
429 rate_limited
The token or its workspace used up a request quota. The response has a Retry-After header and a retry_after_seconds field with the same number of seconds. See Rate limits and quotas.
500 internal_error
VendOrca couldn't complete the operation. Retry with backoff. If it keeps failing, contact support with the request_id.
Errors over MCP
The MCP endpoint reports problems in two ways. Problems with the HTTP request itself, including a bad token, come back as a JSON-RPC error with an HTTP error status. Problems with a single tool call come back as a tool result with isError: true.
JSON-RPC errors
| Situation | HTTP | JSON-RPC code | message |
|---|---|---|---|
| Missing or invalid token | 401 | -32001 | unauthorized, with data.request_id and a WWW-Authenticate header |
| Body larger than 64 KiB | 413 | -32600 | request_too_large |
| Body is not valid JSON, or is JSON but not a JSON-RPC message | 400 | -32700 | parse_error for invalid JSON. A body that isn't a JSON-RPC message gets the MCP transport's own text. |
| Unsupported protocol version | 400 | -32602 | unsupported_protocol_version, with data.supported listing the versions you can use |
Accept doesn't list both application/json and text/event-stream, or Content-Type isn't application/json | 406 or 415 | -32000 | Set by the MCP transport |
| Server failure | 500 | -32603 | internal_error, with data.request_id (a failure inside tools/list arrives at HTTP 200 without data) |
Any HTTP method other than POST or OPTIONS returns 405 with Allow: POST and an empty body.
Tool results with isError
When a tool call is denied, for example for a missing scope, a quota or agent access being off, the HTTP status is 200 and the JSON-RPC call succeeds. The tool result has isError: true, and its text is the same error envelope REST returns:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"isError": true,
"content": [
{
"type": "text",
"text": "{\"error\":{\"code\":\"rate_limited\",\"message\":\"The rate limit for this credential was exceeded. Retry later.\"},\"request_id\":\"…\",\"retry_after_seconds\":12}"
}
]
}
}Parse content[0].text as JSON and handle error.code the same way you would for REST.
Handling errors
| Code | Retry? | What to do |
|---|---|---|
unauthorized | No | Stop. Get a new token from the person who owns the agent. |
not_enabled | No | Stop and tell the user. A Program Admin needs to check Agent access. |
forbidden | No | Don't retry the same call. Tell the user which scope or access is likely missing. |
not_found | No | Fix the tool name or the arguments first. |
rate_limited | Yes | Wait at least retry_after_seconds, then retry. |
internal_error | Yes | Retry with exponential backoff and a cap on attempts. |
Every operation is a read, so retrying a call never changes data.