Errors and how to recover
Every error a Nova integration or AI agent can get, the exact response it arrives in, and what to do next.
Every error arrives in the same envelope (Paging, filtering, errors and rate limits):
{"error": {"code", "message", "request_id", "details"}, "detail": …}.
Switch on error.code first. Where the table says in detail, the
specific reason is inside detail and error.code is the generic one.
The codes
| Status | Where the reason is | What happened | What to do |
|---|---|---|---|
401 |
error.code: unauthorized |
Token missing, expired or revoked; API key revoked or expired | Refresh the OAuth token, or ask an admin for a new key |
403 |
error.code: forbidden + detail.missing_permissions + WWW-Authenticate: … error="insufficient_scope", scope="…" |
The credential was not granted a permission this needs | Ask for the named permission keys (see below) |
403 |
error.code: approval_required |
A person must approve this action: the organization listed it, or Nova guards it with an identity check | Handle an action that waits for a person's approval |
403 |
error.code: phone_number_purchase_requires_approval |
Additional phone-number purchases need explicit Nova platform approval, including on full-access beta plans | Ask Nova support to approve purchases and the number allowance; do not retry purchasing automatically |
403 |
in detail: detail.error: agent_outside_working_hours |
An AI teammate tried to change something outside its working hours | Wait until detail.next_window_starts_at; reads still work |
401, then 403 at /oauth/token |
OAuth error unauthorized_client / invalid_grant, "This agent is paused" |
The AI teammate was paused: its tokens were revoked and it cannot get a new one | Stop; a person has to resume it |
403 |
error.code: forbidden |
The route is not open to software (sign-in, OAuth, platform and credential-management routes) | Nothing to retry; use a person's session |
404 |
error.code: not_found |
No such record in this organization, or a uid of the wrong type | Check the uid and its prefix |
409 |
error.code: account_archived / task_archived |
The record is archived; reads work, changes do not | A person restores it (details.cta says how) |
409 |
error.code: idempotency_payload_mismatch |
Same Idempotency-Key, different body |
Use a new key for a new request |
409 |
error.code: conflict |
The record changed under you, or is in a state that refuses this | Read it again, then decide |
409 |
error.code: email_message_not_in_deleted_items |
Permanent email deletion was requested while the message is outside Deleted Items | Move the message to Deleted Items first; permanent deletion is a separate action |
409 |
error.code: spend_parked |
A paid action (a text, an email) was stopped for a person to review | Nothing to retry; tell a person |
422 |
error.code: validation_error |
A field is missing or wrong; details names it |
Fix the field |
429 |
error.code: rate_limit_exceeded, details.retry_after_seconds |
Per-minute limit, organization limit or daily cap | Wait retry_after_seconds (Rate limits, daily caps and other limits on a credential) |
429 |
error.code: spend_deferred / spend_hold |
The organization's daily budget for a paid action is used, or the provider is switched off | Retry after details.retry_at, or tell a person |
503 |
error.code: service_unavailable, Retry-After: 3 |
Nova is briefly unavailable | Retry after a few seconds with backoff |
A missing permission, in full
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer realm="nova", error="insufficient_scope", scope="billing.invoices.void"
{
"error": {"code": "forbidden",
"message": "This credential does not have the permission this needs.",
"request_id": "…"},
"detail": {
"missing_permissions": [
{"permission": "billing.invoices.void",
"label": "Void frozen billing invoices",
"machine_access": "grant"}
],
"documentation_url": "/help/api-scopes-reference/"
}
}
machine_access: grant means the organization can give your credential
this permission: ask the admin who manages it to add the key. never means
no organization credential can hold it (platform and personal-profile
permissions). To see everything else that key unlocks, search the operation
index for it: Find the operation to call.
Note: read the missing keys from
detail.missing_permissionsor theWWW-Authenticateheader. Do not rely ondetail.code; it is not readable in this release.
Outside working hours, in full
{
"error": {"code": "forbidden",
"message": "This teammate is outside its working hours, so it can read but not change anything right now.",
"request_id": "…"},
"detail": {"error": "agent_outside_working_hours",
"next_window_starts_at": "2026-09-29T13:00:00+00:00"}
}
Retrying safely
- Retry only
429and503, after the time the response gives you. - Never retry
401,403,404,409or422unchanged; they will fail the same way. - Send an
Idempotency-Keyon creates that accept one, so a retry after a dropped connection cannot make a duplicate.