API
Language
Ask for a seat

Closed beta. Nothing here is callable yet. These docs describe the day-one contract, and names marked proposed may change before the beta opens. Ask for a seat

Docs · Guides

Errors and retries

Errors are RFC 9457 problem details. Each one has a type that says what went wrong and a retryable flag that says whether to try again.

Problem details

Error responses use application/problem+json. Decide what to do from type and retryable, not from title or detail, which are for people.

403 · application/problem+json
{
  "type": "https://developers.novo.co/problems/insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This client does not have the cards:freeze scope.",
  "request_id": "req_example_01",
  "retryable": false
}
typestringrequired

Identifies the error. Use it, not the title, to decide what to do

uri

titlestringrequired

Short human-readable summary

statusintegerrequired

The HTTP status code

detailstring

Explanation of this occurrence

instancestring

Identifies this occurrence

request_idstringrequired

Same as the Novo-Request-Id header. Quote it to support

retryablebooleanrequired

Whether the same request may be retried, after Retry-After when present

type is an absolute URI that points to the page for that error (see Problem types). The docs host isn’t final yet TBD, and the short names below are proposed. The token endpoint is the exception: it answers with OAuth 2.0 errors (see Authentication).

Error types

invalid-token401NoThe token is missing, malformed or unknown
token-expired401NoThe token expired. Get a new one and send the request again
client-disabled401NoThe client was revoked, suspended or switched off
insufficient-scope403NoThe client doesn't have the scope this call needs
owner-standing-lost403NoThe owner this client is bound to no longer holds the owner role, or their profile no longer allows access
feature-not-enabled403NoThe API isn't enabled for this business
invalid-parameter400NoA parameter is invalid, for example a card_id that isn't a UUID, or the body isn't valid JSON
not-found404NoNothing with that id in the bound business
idempotency-key-reused409NoThe Idempotency-Key was used before with a different request
idempotency-in-progress409After Retry-AfterThe first request with this key is still running
card-state-conflict409NoThe card is in a state that can't be frozen
payload-too-large413NoThe request body is too large. The size limit will be stated in these docs once it is set
unsupported-media-type415NoThe operation doesn't accept the request body's content type or charset
rate-limited429After Retry-AfterToo many requests for this client or business
internal-error500NoSomething failed unexpectedly on Novo's side. The body carries no internal detail
dependency-unavailable503After Retry-AfterPart of the answer can't be loaded right now

When to retry

  • Retry only when retryable is true, and wait for Retry-After when it’s there.
  • 503 always means a temporary problem on Novo’s side. A revoked or switched-off client is a 401, never a 503, so a loop that retries on 503 can’t keep hammering with a dead client.
  • For token-expired, get a new token and repeat the call. For any other 401 or 403, stop: retrying won’t change the answer.
  • If you run an agent, give it this rule rather than letting it retry on every error.

Rate limits

Limits apply per client and per business. When you go over, the API returns 429 with Retry-After, plus RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset headers. The numbers are TBD. The token endpoint has a limit of its own, so cache your access token for its whole lifetime.

Idempotency

Freezing a card accepts an Idempotency-Key header: a value you choose, up to 255 characters, such as a UUID. It’s recommended, not required. Freezing is safe to repeat anyway: freezing a frozen card returns 200.

  • Send the same key when you retry the same request, and a new key for a new request.
  • If the key was already used with a different request, you get 409 idempotency-key-reused.
  • If the first request with that key is still running, you get 409 idempotency-in-progress. Retry after Retry-After.
  • Keys are kept for 24 hours proposed.