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

# Errors

> What a refused request looks like, which status codes to expect, and how to read the reason.

A refused request answers with an HTTP status and a JSON body saying why. The body has the same four
keys whatever went wrong:

```json theme={null}
{
  "code": "PT400",
  "message": "Category, Site is required",
  "details": "{\"fields\": [\"Category\", \"Site\"], \"error_code\": \"MISSING_FIELD\"}",
  "hint": ""
}
```

| Key | What it holds |
| - | - |
| `message` | A sentence for a person. Show it, or log it |
| `details` | A JSON object **written as a string**. Parse it and read `error_code`, which is stable enough to branch on. It may also name the fields involved or the permission that was missing |
| `hint` | What to do about it, where there is something to do. Often empty |
| `code` | The server's own code. Prefer `error_code` from `details` |

## Status codes

| Status | When | Typical `error_code` |
| - | - | - |
| 400 | The request is incomplete or names something it may not set | `MISSING_FIELD`, `UNKNOWN_FIELD`, `INVALID_PAYLOAD` |
| 401 | No token, or one that is malformed or expired | — |
| 403 | The account does not hold the permission. `details` names it | `FORBIDDEN` |
| 404 | The record does not exist, or this account cannot see it | `NOT_FOUND` |
| 409 | The request is valid, and the record's current state or your organisation's rules refuse it | `TRANSITION_REFUSED`, `MOVE_INSTEAD` |
| 422 | A value is well formed and still not one this record accepts, or something it depends on is missing | `CUSTODIAN_REQUIRED` |

`exchange_api_key` is the exception. It answers a refused key with HTTP 200 and an `error_code` in
the row, as [Authentication](/api-reference/authentication) describes.

## Refusals your organisation decides

Most 409s come from how your organisation set the product up rather than from the API. The same work
order can be refused for one organisation and accepted for another. These are real answers from a
demo organisation:

```json theme={null}
{ "message": "WO-2026-00028 cannot move from Assigned to In Progress",
  "hint": "That move is not allowed from its current status. Choose another status, or ask an administrator to allow it on the Statuses screen." }
```

```json theme={null}
{ "message": "WO-2026-00028 cannot move from In Progress to Completed: Record what was wrong before closing this — the asset is critical." }
```

```json theme={null}
{ "message": "This job cannot be completed until these are filled in: Production Batch" }
```

The first is the organisation's statuses, the second is one of its rules and the third is one of its
own fields. Before you move a work order, ask `available_transitions` which moves it allows and
what each one needs. [Raise and complete work](/api-reference/guides/raise-and-complete-work) shows how.

## Retrying

Retry a 401 after getting a new token. Do not retry a 400, 403 or 409 unchanged: it will be refused the
same way until the request, the account or the record changes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.