Errors

The canonical error envelope
View as Markdown

Standard API errors share this envelope:

1{
2 "error": "forbidden",
3 "message": "You do not have permission to perform this action.",
4 "request_id": "8f9e4c2b-0c1a-4f3e-9a8d-1b2c3d4e5f60",
5 "docs_url": "https://docs.lucanto.eu/api/v1/errors/forbidden"
6}
  • error — a stable, machine-readable code. Branch on this, not the message.
  • message — human-readable; wording may change between releases.
  • request_id — also returned as the X-Request-Id header. Quote it in support tickets so we can pull the matching server log line.
  • docs_url — deep link to the per-code documentation page.

Validation failures add an errors array of individual messages.

Plan and feature limits

Feature gates use a separate actionable payload with a stable code, the affected feature_key, and an upgrade_url:

1{
2 "error": "Limit reached for ai_expense_extractions",
3 "code": "feature_limit_exceeded",
4 "feature_key": "ai_expense_extractions",
5 "plan": "free",
6 "upgrade_url": "https://app.lucanto.eu/42/billing/plans"
7}

Branch on code for these responses. Period-based limits can also include current_usage, limit, and resets_at, with Retry-After set when a reset time is available. Unlike the standard envelope, feature-gate payloads do not include request_id or docs_url.

Status codes

StatuserrorMeaning
400bad_requestMalformed request
401unauthenticatedMissing or invalid token
402subscription_requiredWorkspace has no active subscription
403forbiddenToken scope or role doesn’t permit this action
404not_foundResource doesn’t exist or isn’t visible to you
409conflict / idempotency_key_conflict / idempotency_in_progressConflicting state (see Idempotency)
422unprocessable_entityValidation failed (see errors array)
429rate_limited / feature_limit_exceededThrottled or plan limit reached

Cross-workspace access returns 404, not 403, so the API never leaks the existence of records you can’t see.