# Error codes

This page covers errors from two sources: the authorisation server, when obtaining or refreshing a token, and the Partner API, when making requests.

## OAuth errors

Errors from the token endpoint return a JSON object with an `error` value and, usually, an `error_description`:

```json
{
  "error": "invalid_grant",
  "error_description": "The refresh token is invalid."
}
```

Errors from the authorisation endpoint are returned as an `error` parameter appended to your redirect URI.

These values are defined by the OAuth 2.0 specification ([Section 4.1.2.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1) for authorisation, [Section 5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) for tokens).

| Error | Meaning |
|  --- | --- |
| `invalid_request` | The request is missing a required parameter, repeats one, or is otherwise malformed. |
| `invalid_client` | Client authentication failed. |
| `invalid_grant` | The authorisation code or refresh token is invalid, expired, already used, revoked, or does not match the redirect URI used to obtain it. |
| `unauthorized_client` | Your client is not permitted to use this grant type. |
| `unsupported_grant_type` | The grant type is not supported. |
| `invalid_scope` | The requested scope is invalid, unknown, or outside the set your client is configured for. |
| `access_denied` | The user declined to grant access. |


### Common causes

**`invalid_client`** — usually a credentials mismatch rather than a malformed request. Check that you are using the credentials for the environment you are calling: Test credentials sent to the production host, or the reverse, produce this error. If you are using a Basic header, check the Base64 encoding and that the word `Basic` is capitalised.

**`invalid_grant` when refreshing** — refresh tokens are single-use. Once exchanged, the old one is dead. Confirm you are storing and using the new refresh token from each response, and that only one process refreshes at a time. See [Managing Access and Refresh Tokens](/products/nowinfinity/resources/refresh-tokens).

**`invalid_grant` when exchanging a code** — authorisation codes are single-use and short-lived. This also occurs when the `redirect_uri` in your token request does not exactly match the one used in the authorisation request.

**`invalid_scope`** — you requested a scope your client is not configured for. Check [Authorisation Scopes](/products/nowinfinity/resources/authorisation-scopes) and, if you need a scope you do not have, contact us.

**`access_denied`** — the customer declined on the consent screen. This is a normal outcome, not a fault. Handle it gracefully and let them try again.

## API errors

Errors from the Partner API return a JSON body with a `Message`:

```json
{
  "Message": "Authorization has been denied for this request."
}
```

### Validation errors

A `400` from an endpoint that accepts a body also includes `ModelState`, mapping each invalid field to its error messages:

```json
{
  "Message": "The request is invalid.",
  "ModelState": {
    "Trustee.Individuals[0].LastName": [
      "'Last Name' must not be empty."
    ],
    "AllocationDetails.PrimaryBeneficiaries": [
      "Primary Beneficiary percentage total must equal 100."
    ]
  }
}
```

Keys use dotted and indexed paths mirroring your request body, so they can be mapped back to fields in your own form. Note that `ModelState` carries both field-level validation and cross-field business rules — the second example above is a rule about a group of values, not a single property.

Surface these messages to your users rather than a generic failure. They are specific enough to be actionable.

### Status codes

| Status | Cause | What to do |
|  --- | --- | --- |
| `400` | Invalid request body. | Read `ModelState` and correct the fields identified. |
| `401` | Missing, expired or invalid access token. | Refresh the token and retry. If refresh fails, start a new authorisation. |
| `403` | Valid token, insufficient access. | Check your granted scopes, and whether the resource belongs to an account you can reach. |
| `404` | Resource not found. | Check the identifier, and check that `X-NI-AccountID` targets the account the record belongs to. |
| `202` | Not an error. Files are still generating. | Retry later, backing off between attempts. |
| `204` | Not an error. Files do not exist because the document is still in draft. | Submit the form to `/forms/process` first. |


## Related

- [API structure](/products/nowinfinity/resources/api-structure) — status codes in context.
- [Managing Access and Refresh Tokens](/products/nowinfinity/resources/refresh-tokens) — avoiding `invalid_grant`.
- [Authorisation Scopes](/products/nowinfinity/resources/authorisation-scopes) — avoiding `invalid_scope`.