Skip to main content

API errors

Every error response from an authenticated /api/* endpoint follows the same shape, defined by ApiErrorSchema in packages/shared/src/api/errors.ts:

{
"error": {
"code": "UNAUTHENTICATED",
"message": "Human-readable description",
"requestId": "req_...",
"details": { "optional": "per-error context" }
}
}

The code field is a stable SCREAMING_SNAKE_CASE identifier — safe to key off programmatically. The message is for humans and may change between releases. The requestId is included when the request reached the error handler and is useful when filing support tickets. The details object is populated only for errors that carry extra context (validation failures, plan limits).

Public /v1/public/* endpoints return a smaller version of this shape — just { "error": { "code": "..." } } — because the widget does not need human-readable messages and because these endpoints are CORS-unrestricted. The error codes still come from the same enum where they overlap.

Status codes

StatusCodeWhen it happens
400 Bad RequestVALIDATION_ERRORBody failed Zod validation or required field missing
401 UnauthorizedUNAUTHENTICATEDMissing, empty, or invalid Bearer token on /api/*
402 Payment RequiredPLAN_LIMIT_REACHEDAgency is at its plan limit for the resource
403 ForbiddenFORBIDDENToken is valid but lacks access to the resource
403 ForbiddenNO_AGENCYAuthenticated user has no agency attached to their profile
404 Not FoundNOT_FOUNDResource does not exist or is soft-deleted
409 ConflictCONFLICTDuplicate resource (e.g., second bot for the same clientUrl)
429 Too Many RequestsRATE_LIMITEDSee Rate limits
500 Internal Server ErrorINTERNAL_ERRORServer-side bug; retry with exponential backoff
503 Service UnavailableTEMPORAL_UNAVAILABLEBackground worker (Temporal) is unreachable
503 Service UnavailableUPSTREAM_UNAVAILABLEA dependent upstream service is unreachable

Validation errors

When Zod validation fails, the 400 response's details field carries the Zod issues so you can surface field-level errors in your UI:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": {
"issues": [
{ "path": ["clientUrl"], "message": "Invalid url" }
]
}
}
}

Plan limit errors

When the agency has hit a plan ceiling — bots, monthly messages, active actions — the server returns 402 Payment Required with code PLAN_LIMIT_REACHED. The message names the resource and the current limit. The correct response is to surface an upgrade nudge, not to retry.

Retries

  • 5xx errors: retry with exponential backoff (1s, 2s, 4s, 8s, 16s).
  • 429: wait exactly Retry-After seconds, then retry once.
  • 4xx other than 429: do not retry — fix the request.