API REFERENCE
Error reference
Every error code the API can return, what it means, and the usual fix. Errors come back as JSON with a stable code you can switch on.
When a request fails, the API returns a JSON body with a stable code and a human message. Switch on the code, not the message, since messages can change. On the /v1/chat stream a failure arrives as an SSE error event before the stream closes, carrying the same code in its content.
json
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Try again shortly."
}
}Project and origin
| Field | Type | Description |
|---|---|---|
PROJECT_INVALID_KEY | 401 | The Bearer key is missing, malformed, or does not match a project. Check that you sent Authorization: Bearer era_... and that the key is current. |
ORIGIN_NOT_ALLOWED | 403 | The request came from an origin not in the project allowlist. Add the domain in Settings, or route through your own proxy with data-base-url. |
PROJECT_NO_API_KEY | 400 | No working model key is configured, so the agent cannot run. Add a provider key (Anthropic, OpenAI, or Google) in Settings. The agent tries keys in priority order and health-checks them. |
Identity and signatures
These surface on signed-mode features: private knowledge, the vault, per-user MCP OAuth, and scheduled tasks.
| Field | Type | Description |
|---|---|---|
IDENTITY_NOT_CONFIGURED | 400 | The project has no identity settings for the mode you are using. Configure open or signed identity in Settings before sending a user_context that needs it. |
IDENTITY_ID_REQUIRED | 400 | The user_context has no id. Every identity needs a stable user.id, even in open mode. |
SIGNED_IDENTITY_REQUIRED | 403 | You called a signed-only endpoint (knowledge, credentials, tasks, per-user OAuth) with an unsigned identity. Sign the user_context and retry. |
IDENTITY_SIGNATURE_REQUIRED | 401 | The project is in signed mode but the user_context has no _sig. Attach an HMAC signature. |
IDENTITY_SIGNATURE_INVALID | 401 | The _sig did not verify. Usually a canonicalization mismatch (sort keys, exact bytes) or the wrong signing secret. Recheck the stable-stringify recipe. |
IDENTITY_SIGNATURE_EXPIRED | 401 | The _ts is outside the 300-second window. Sign the identity server-side just before the request, and check clock skew. |
If signatures fail only sometimes, the cause is almost always non-canonical JSON (unsorted keys or added whitespace) or a clock that drifts past 5 minutes. The signing helper is in the signed identity guide.
Rate limits and budget
| Field | Type | Description |
|---|---|---|
RATE_LIMIT_EXCEEDED | 429 | A per-project rpm/rpd limit or the per-user chat throttle was hit. Back off and retry. Raise the limits on a higher plan if you need more headroom. |
BUDGET_EXCEEDED | 402 | The monthly token budget hard stop tripped. Chat is refused until the budget resets or you raise it in Settings. Because you bring your own model key, this is your own guardrail, not an EERRAA charge. |
SESSION_NOT_FOUND | 404 | The session_id does not exist or belongs to a different user. Omit session_id to start a new session, or use one returned by a prior "session" event. |
Knowledge uploads
| Field | Type | Description |
|---|---|---|
FILE_TOO_LARGE | 413 | The upload is over the 50 MB cap. Split or compress the document. |
UNSUPPORTED_TYPE | 415 | The file is not PDF, TXT, or MD. Convert it to a supported type first. |
DOC_CAP_REACHED | 409 | The user already has 20 documents. Delete one with DELETE /v1/knowledge to free a slot. |
Tools
| Field | Type | Description |
|---|---|---|
TOOL_UNAVAILABLE | 400 | The agent tried to call a tool that could not be reached, for example an MCP server that is down or a webhook that failed to respond. Check the tool connection and logs. |
TOOL_DISABLED | 400 | The tool exists but is turned off for this project. Enable it on the Tools page. Newly discovered MCP tools import disabled by design, so you opt in to each one. |
Tool errors do not crash the reply. The agent sees the failure, can tell the user, and may try another path. If a write tool is waiting on approval it stays paused until the user hits Approve or Cancel in the widget.
