Errors
Response shape
Section titled “Response shape”All errors follow:
{ "error": "Human-readable message.", "code": "ERROR_CODE", "details": { "...": "..." }}details is optional and contains context (validation failures, the rejected field, etc.).
Code reference
Section titled “Code reference”| Code | HTTP | When |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid credentials. |
FORBIDDEN | 403 | Authenticated but no access to this resource. |
NOT_FOUND | 404 | Resource lookup empty. Also returned when ownership check fails (to avoid leaking existence). |
VALIDATION_FAILED | 400 | Bad input. details usually carries the field-level reason. |
RATE_LIMITED | 429 | Per-caller RPM exceeded. See Rate limits. |
CONCURRENCY_LIMIT | 429 | Profile concurrency limit hit — too many simultaneous workspaces. |
BUDGET_EXCEEDED | 402 | Task hit max_budget_usd. |
TURN_LIMIT_EXCEEDED | 422 | Task hit max_turns. |
CONTAINER_FAILED | 502 | Docker couldn’t start / agent process crashed. |
MODEL_ERROR | 502 | Anthropic/Ollama returned an error. |
BAD_GATEWAY | 502 | Third-party integration error (e.g. Teller, Gmail). |
INTERNAL_ERROR | 500 | Server crashed in an unexpected place. Please report. |
On 404 for ownership failure
Section titled “On 404 for ownership failure”/v1/workspaces/:id, /v1/profiles/:id, etc. return 404 when the requested resource doesn’t belong to the caller. This is intentional: 403 would confirm the row exists; 404 doesn’t. Treat 404 as either “doesn’t exist” or “exists but not yours.”
On 429s
Section titled “On 429s”The platform has two RPM windows:
- Per-caller — across all your endpoints, default 60 req/min with a burst of 10. Tunable via env.
- Per-profile concurrency — across simultaneous running workspaces, default 5.
If you’re polling task status in a loop, sleep at least 1 second between requests; for long-running tasks the WebSocket subscription is cheaper.
Idempotency
Section titled “Idempotency”The platform doesn’t honor explicit Idempotency-Key headers (yet). Operations that are naturally idempotent:
DELETE /v1/*— repeating a delete returns 404 the second time, not 500.POST /v1/playbooks/:id/run— each call fires a separate run; if you want exactly-one execution, deduplicate at your end.
For task submission, use the WebSocket — it’s harder to accidentally double-submit because the connection is stateful.