Error codes
The API returns errors in one shape:
{ "error": { "code": "origin_not_allowed", "message": "this origin is not registered for the project" }}Use error.code in code. message is for people and can change.
Authentication
| Status | Code | Meaning |
|---|---|---|
401 | missing_key | No Gutternote-Key header. |
401 | unknown_key | The project key is not recognized. |
403 | origin_not_allowed | The origin is not in the project’s sites. |
401 | no_session | A write has no Gutternote-Session. |
401 | session_expired | The session expired. Sign in again. |
401 | invalid_session | The session token failed verification. |
403 | session_project_mismatch | The session belongs to another project. |
403 | verification_required | The project does not allow guests. |
409 | google_not_enabled | The project does not use Google sign-in. 403 on renew. |
501 | google_not_configured | This deployment has no Google sign-in. |
400 | missing_credential | A Google sign-in has no credential. |
401 | wrong_audience, invalid_credential | The Google token failed verification. |
403 | email_unverified | Google has not verified the email. |
403 | domain_not_allowed | The email’s domain is not on the project’s list. |
409 | own_idp_not_enabled | The project does not use your own identity provider. |
403 | bikeshed_required | Your own identity provider needs the Bikeshed plan. |
503 | billing_unavailable | The plan could not be checked. Retry. |
503 | no_signing_secret | The project has no identity signing secret. |
400 | missing_token | The token exchange has no token. |
401 | token_expired | The identity token expired or is older than the maximum age. |
401 | invalid_token | The identity token failed verification. |
401 | session_not_renewable | Sessions from your own identity provider are not renewed. Get a new token. |
403 | member_required | Only workspace members can triage, resolve, stop a task or send work. |
503 | membership_unavailable | Membership could not be checked. Retry. |
503 | read_only | The deployment refuses writes for now. |
Threads
| Status | Code | Meaning |
|---|---|---|
400 | invalid_json | Bad JSON, an unknown field, a requestId that is not a UUID, or a body over 256 KiB. |
400 | missing_path | The list has neither path nor site=true. |
400 | invalid_deployment | deploymentId is not a UUID. |
400 | invalid_pr | prNumber is not a positive integer. |
400 | invalid_id | A thread, comment or mention ID has the wrong shape. |
400 | invalid_anchor | The anchor cannot be decoded. |
400 | invalid_request | A new thread fails validation. |
400 | empty_body | A reply has no text and no attachment. |
400 | invalid_status | Status is not open, resolved or archived. |
400 | invalid_emoji | The reaction is not an accepted emoji. |
400 | foreign_attachment | The attachment URL did not come from this API. |
403 | not_author | You can delete only your own comment. |
404 | not_found | The thread, comment or attachment is not in this project, or a retried comment was deleted. |
409 | unsupported_by_backend | The project’s comment store cannot do this. |
409 | not_running | The agent task is finished, already stopping, or not in this thread. |
409 | request_in_progress | This requestId is still being saved. Retry after Retry-After seconds. See Retry. |
409 | request_id_reused | This requestId already named a different thread or reply. Use a new UUID. |
502 | body_store_unavailable | The project’s comment store failed. Retry. |
Uploads
| Status | Code | Meaning |
|---|---|---|
400 | unsupported_type | The content type is not accepted. |
400 | invalid_kind | The attachment kind is not recognized. |
413 | too_large | The size is not between 1 byte and uploads.maxBytes. |
501 | uploads_unavailable | This deployment has no image storage. |
MCP tool errors
A failed tool call on /mcp is an MCP tool error, not the JSON shape above.
The message starts with a stable code:
| Code | Meaning | What to do |
|---|---|---|
PROJECT_REQUIRED | The connection needs a project choice. | Call list_projects and pass projectId on each call. |
PROJECT_NOT_AVAILABLE | The project is not enabled for this connection, or does not exist. | Call list_projects again. Ask a workspace owner for access. |
GUIDANCE_UNAVAILABLE | The server could not check the project’s workflow. | Retry list_projects. |
THREAD_NOT_FOUND | No such thread in this project, or agents cannot see it. | Stop. Check the ID. |
THREAD_VERSION_CONFLICT | Another agent wrote to the thread after you read it. | Call get_thread, then retry with the new version. |
TOOL_TIMEOUT | The call ran past the per-call time limit and was stopped. | Wait a few seconds, then call it again. Retry a write with the same clientEventId (requestId on batch tools): if the first attempt went through, the retry returns its result instead of doing it twice. |
WILDCARD_ORIGIN_NEEDS_DASHBOARD | add_allowed_origin got an origin with *. Nothing was saved. | Ask the person to add the pattern on Configure. |
Other tool errors name the bad argument in plain words.
/mcp endpoint
/mcp returns 401 with a WWW-Authenticate header that points to the OAuth
metadata. The body uses the JSON shape:
| Code | Meaning |
|---|---|
missing_token | No bearer token. |
invalid_token | Gutternote did not issue this token. |
expired_token | The access token expired. The client refreshes it. |
revoked_token | The connection was revoked. Approve it again. |
The OAuth endpoints return standard OAuth errors, such as invalid_grant,
invalid_request and invalid_client_metadata.
Server failures
500 internal means the server could not finish the request. Keep the request
ID when you report it.