> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zivio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> The error envelope and how to respond to each status

### The error envelope

Every non-2xx response uses the same shape. Only `error` is always present; the remaining
fields appear when they add something useful.

<ResponseField name="error" type="string">
  A stable machine-readable code, for example `insufficient_scope`. Branch on this, not on
  the human-readable text.
</ResponseField>

<ResponseField name="error_description" type="string">
  A human-readable explanation, safe to log.
</ResponseField>

<ResponseField name="message" type="string">
  A shorter human-readable summary, present on some responses.
</ResponseField>

<ResponseField name="details" type="object">
  Additional structured context — for validation failures, typically the offending fields.
</ResponseField>

<ResponseField name="required_scope" type="string">
  On `403`, the scope this endpoint needs.
</ResponseField>

<ResponseField name="provided_scopes" type="array">
  On `403`, the scopes your token actually holds.
</ResponseField>

### Status codes

| Status               | Meaning                                                | What to do                                               |
| -------------------- | ------------------------------------------------------ | -------------------------------------------------------- |
| `400`                | Malformed request or bad token exchange                | Fix the request; do not retry unchanged                  |
| `401`                | Token missing, expired, revoked or malformed           | Get a new token, then retry once                         |
| `403`                | Valid token, insufficient scope                        | Request a token with the scope named in `required_scope` |
| `404`                | No such record, or not visible to the acting user      | Check the ID and the user's permissions                  |
| `404 Tenant Unknown` | `zivio-tenant-id` missing or unrecognised              | Add the header; checked before authentication            |
| `405`                | Method not allowed on this path                        | Check the method against the API Reference               |
| `422`                | Validation failed, or the record is in the wrong state | Read `details` and correct the payload                   |

<Note>
  A `404` does not always mean the record is absent — responses are scoped to what the
  acting user may see, so a record outside their permissions is indistinguishable from one
  that does not exist.
</Note>

### Worked examples

<AccordionGroup>
  <Accordion title="404 Tenant Unknown — before you get any further">
    A plain-text `Tenant Unknown` body, rather than the JSON envelope above, means the request
    never reached the API. The `zivio-tenant-id` header is missing or does not match a Zivio
    organisation, and it is checked before your token is.

    Your token and scopes are not the problem here. See
    [Identifying your organisation](/v4/authentication#identifying-your-organisation).
  </Accordion>

  <Accordion title="401 — expired token">
    ```json theme={null}
    {
      "error": "invalid_token",
      "error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons"
    }
    ```

    Request a fresh token and retry. Retry once only — a second `401` means the credentials
    or the `Authorization` header are wrong, not the token's age.
  </Accordion>

  <Accordion title="403 — missing scope">
    ```json theme={null}
    {
      "error": "insufficient_scope",
      "error_description": "The request requires higher privileges than provided by the access token",
      "required_scope": "projects:read",
      "provided_scopes": ["welcome:read"]
    }
    ```

    Retrying will not help — the token needs to be issued with `projects:read`. See
    [Scopes](/v4/scopes).
  </Accordion>

  <Accordion title="422 — wrong state for the action">
    Workflow endpoints reject actions that do not apply to the record's current state. For
    example, submitting a project that is not in `draft`, or one that fails publish
    validation.

    Some `422` responses are actionable rather than fatal: `POST /projects/{id}/submit`
    returns `error: approver_selection_required` together with an `approver_selection` array
    listing each step's eligible approvers, so you can re-submit with `approvers` populated.
  </Accordion>
</AccordionGroup>

### Handling errors well

* Branch on the `error` code, never on `error_description` wording.
* Retry `401` once after refreshing the token. Do not retry `403` or `422` — they need a
  different token or a different payload.
* Log `error`, `error_description` and `details` together; `details` usually names the exact
  field at fault.
