# Error handling

The MK.IO APIs return the same error body for every failure. Once your client handles that one shape, the rest of error handling is consistent across the platform: inspect the HTTP status, branch on the machine-readable code, log the human-readable detail, and keep the request reference for follow-up.

## The standard error body

When an operation fails, the API returns a JSON object with this shape:

```json
{
  "error": {
    "code": "<error_code>",
    "detail": "<error_message>",
    "extraDetail": {}
  },
  "status": 400,
  "ref": "<request_reference>"
}
```

Each field has a distinct job:

| Field               | Use it for                                      |
| :------------------ | :---------------------------------------------- |
| `error.code`        | Programmatic branching in your application.     |
| `error.detail`      | Logs, dashboards, and operator-facing messages. |
| `error.extraDetail` | Any additional context the API includes.        |
| `status`            | The HTTP status code, repeated in the body.     |
| `ref`               | Support follow-up and request tracing.          |

If you keep only one field beyond the status line, keep `ref`. It is the quickest way to identify a failing request afterwards.

## Status codes across the platform

The exact response set varies by endpoint, but these codes appear throughout the APIs.

Success responses:

| Code  | Meaning                                                |
| :---- | :----------------------------------------------------- |
| `200` | The request succeeded and returned a body.             |
| `201` | The resource was created.                              |
| `202` | The request was accepted and continues asynchronously. |
| `204` | The request succeeded with no response body.           |

Client-side problems:

| Code  | Meaning           | What to do next                                                                            |
| :---- | :---------------- | :----------------------------------------------------------------------------------------- |
| `400` | Bad Request       | Recheck the path, query parameters, and body format.                                       |
| `401` | Unauthorized      | Recheck the bearer token and `Authorization` header.                                       |
| `403` | Forbidden         | Recheck organization, project, and operation access for the token's user.                  |
| `404` | Not Found         | Recheck names and path parameters, especially project-scoped names.                        |
| `409` | Conflict          | Recheck the resource state, or whether the operation conflicts with an existing reference. |
| `429` | Too Many Requests | Back off and retry later. See [Rate limits](/api-guides/understanding/rate-limits).        |

Server-side problems:

| Code  | Meaning               | What to do next                                                |
| :---- | :-------------------- | :------------------------------------------------------------- |
| `500` | Internal Server Error | Retry carefully and keep the `ref` value.                      |
| `503` | Service Unavailable   | Treat it as temporary and keep the `ref` value if it persists. |

## A handling order that works

When a request fails:

1. Record the HTTP status code.
2. Parse `error.code` and `error.detail`.
3. Record `ref`.
4. Decide whether to fix the request, retry it, or surface it to an operator.

As a guide to that decision:

- `400`, `401`, `403`, and many `404` responses mean the request needs correcting.
- `409` usually means the operation is valid, but the target resource is not in the right state yet, or is still referenced elsewhere.
- `429`, `500`, and `503` are the cases where careful retry logic pays off.

## See the status and body together

When you debug from the command line, print the transport status alongside the body so a log line captures both:

```bash
curl -sS \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json" \
  -w "\nHTTP Status: %{http_code}\n" \
  "https://app.mk.io/api/v1/projects/<PROJECT_NAME>/media/assets/nonexistent"
```
