Errors

One envelope for every error, with stable machine-readable types.

Every error response (auth failures, validation rejections, rate limits, server faults) carries the same JSON envelope:

1{
2 "error": {
3 "type": "not_found",
4 "message": "No borrower with that id exists in this workspace.",
5 "request_id": "req_0123456789abcdef01234567"
6 }
7}
  • type: a stable, machine-readable identifier. Branch on this, never on message.
  • message: human-readable detail. Wording can change without notice.
  • request_id: matches the X-Request-ID response header. Include it when contacting support; it lets us find the exact request.
  • details: optional object carrying the narrow, contract-level context a caller needs in order to act (for example current_version on a 409 version conflict). Absent when there is nothing actionable to add.

Adding a new type is a non-breaking change; renaming or removing one is breaking and versioned. Treat an unrecognized type according to its HTTP status class.

Status codes

StatusMeaning
400The request is malformed or fails validation (invalid_request, invalid_cursor, invalid_limit, invalid_version, …)
401Missing or invalid credentials (unauthorized)
402The workspace has no active subscription (billing_required, subscription_required)
403Authenticated but not allowed (forbidden, insufficient_scope, plan gates)
404The resource does not exist in your workspace (not_found). Also returned rather than 403 for ids you cannot see
409The write conflicts with current state (conflict, idempotency_request_mismatch)
413Body or file over the size cap (payload_too_large)
422Understood but not processable under a domain rule (unprocessable)
423The spread board is locked by a finalize (spread_locked). Reopen it first
429Rate limited. Honor Retry-After. See Rate limits
5xxServer fault (internal_error). Safe to retry mutations that carried an Idempotency-Key

Common error types

typeStatusWhen
unauthorized401Missing, malformed, revoked, or wrong-environment API key
forbidden403The caller’s role or session kind cannot perform this action
insufficient_scope403The API key lacks a required scope. The message names it
mode_mismatch403The key’s mode cannot call this endpoint. Async exports, for example, run on live keys only
billing_required402The workspace has no active subscription. Set up billing, then retry
subscription_required402The same condition on the upload and intake endpoints, returned as { "error": "subscription_required", "message": … } rather than the envelope
spreads_not_in_plan / embed_not_in_plan / counterparty_not_in_plan403The feature is not in the workspace’s plan
rate_limited429Over budget for the endpoint’s policy, or throttled. See Rate limits
invalid_request400Failed validation; message names the field
invalid_cursor / cursor_expired / invalid_limit400Pagination inputs: restart iteration without a cursor
invalid_version400Unrecognized SpreadSpace-Version header value
not_found404No such resource in your workspace
conflict409Stale version or duplicate state transition; details.current_version when applicable
idempotency_in_progress429A request with the same Idempotency-Key is still executing. Retry after Retry-After
idempotency_request_mismatch409The same Idempotency-Key was reused with a different request body
idempotency_response_too_large500The original response is too large to replay; retry with a new key
payload_too_large413Over the request or upload size cap
unprocessable422A domain rule rejected the request; details carries the operative value
internal_error500Unexpected server fault. Retry with backoff, then contact support with the request_id

Embed-session minting and exchange have their own type family (invalid_embed_token, embed_token_expired, embed_scope_not_allowed, invalid_session_handle, …) documented alongside the Embed flow.