BetterClaw
Agent API

Errors

Agent API status codes, error bodies, and what to do about each.

Errors

Failed requests return a non-2xx status and a JSON body with a statusCode and a readable message:

{ "statusCode": 403, "message": "API key is missing scope(s): agents:read", "error": "Forbidden" }

Branch on the status code. Messages are for people and may be reworded.

Authentication and permission errors

StatusMessageCause and fix
401Missing auth tokenNo Authorization header. Add Authorization: Bearer <key>.
401Invalid or expired tokenThe value isn't a BetterClaw key at all, often a typo or a missing Bearer prefix.
401Invalid API keyThe key was revoked, or never existed. Generate a new one.
401The user this API key acts as no longer has access to the workspaceThe key's creator left the workspace. Revoke it and generate a new one.
401Invalid or expired SDK tokenThe session token expired or was altered. Mint a new one.
401An API key must be sent in the Authorization header, not a query parameterMove the key out of the URL.
403An API key must not be used from a browser — exchange it for a session tokenThe request had an Origin header. Use a session token in browsers.
403This endpoint cannot be used with an API keyThe route isn't part of the Agent API, for example managing keys or billing. Use the app.
403API key is missing scope(s): …Generate a key (or mint a token) with the listed permission.
403This API key is not bound to that workspaceThe request names a different workspace than the key's.
403This API key is pinned to a different agentThe request names an agent other than the one the key is pinned to.
404Not foundThe chat belongs to another workspace, or to an agent the key isn't pinned to.

Revoking a key takes effect on its very next request, and on every session token minted from it.

Request and resource errors

StatusWhen
400A required field or query parameter is missing, or a message has neither text nor files.
402The workspace is out of credits for the month, payment is required to resume it, or the agent is locked on the Free plan.
404The chat, message, or deliverable doesn't exist, or was deleted.
409A clientRequestId was reused with different content.
410A retried request refers to a chat that has since been deleted.
413An attachment is over 30 MB, or the files in one request are too large together.
415An attachment's type isn't accepted.
422The body failed validation; the response lists each problem in issues. (POST /chats answers 400 instead.)
429Too many failed token exchanges from your IP address. Wait retryAfter seconds.
5xxSomething went wrong on our side. Retry with a back-off, using a clientRequestId for sends.

A 402 for a workspace that needs payment includes a code:

{
  "statusCode": 402,
  "code": "BILLING_REQUIRED",
  "workspaceId": "6f1c2d4e-8a90-4b1c-9d2e-3f4a5b6c7d8e",
  "message": "Payment is required to resume this workspace."
}

A 422 lists each invalid field:

{
  "statusCode": 422,
  "error": "Unprocessable Entity",
  "message": "Validation failed",
  "issues": [{ "path": "ttlSeconds", "message": "Number must be less than or equal to 900" }]
}

Replies that fail

A reply can fail after POST /chats/{chatId}/messages succeeded, for example because the agent couldn't start or its model provider returned an error. The request itself still returns 201; the assistant message then ends with status: "error" and an errorMessage. Check for it when you read the reply or in the stream.

Stream close codes

WebSocket failures arrive as close codes rather than HTTP statuses. See Close codes.