# Errors

Status codes, the error body, and the cases that need handling in code.

Canonical: https://chikaraintel.com/docs/api/errors

## The error body

A failed call returns a non-2xx status and an `errors` array. Quote the `correlation_id` if you contact us about a failure.

404:

```json
{
  "status": {
    "code": 404,
    "messages": []
  },
  "errors": [
    {
      "message": "Organisation not found",
      "code": ""
    }
  ],
  "correlation_id": "5f0c2a9e4b7d4c1e"
}
```

## Status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | The request was malformed, such as an invalid search chip | Fix the request. The message says what's wrong |
| 401 | The token is missing, malformed or revoked | Check the `Authorization` header. See [authentication](/docs/authentication) |
| 404 | No record has that reference, or the path doesn't exist | Check the reference. Don't retry |
| 422 | A parameter has a value outside its allowed list | Use one of the values listed in the reference |
| 429 | Too many requests | Wait and retry with backoff. See [limits](/docs/limits) |
| 500 | Something failed on our side, or a malformed date | Check dates are `YYYY-MM-DD`, then retry once. If it persists, contact us with the `correlation_id` |

## Known rough edges

- An invalid date, such as `2026-02-30`, returns a 500 with a database message instead of a 400. Validate dates before sending.
- Some list filters quietly ignore a value they don't recognise and return the unfiltered list. Check the parameter reference for allowed values.
- `GET /v1/profiles/{profileReference}/ownership` returns an empty array for an unknown reference, not a 404.

## Handling errors in code

**JavaScript**

```javascript
const res = await fetch("https://api.chikaraintel.com/v1/organisations/3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01", {
  headers: { Authorization: `Bearer ${process.env.CHIKARA_API_TOKEN}` }
});

if (!res.ok) {
  const body = await res.json().catch(() => ({}));
  const message = body.errors?.map((e) => e.message).join("; ") || res.statusText;
  throw new Error(`${res.status}: ${message} (correlation ${body.correlation_id ?? "none"})`);
}

const { success } = await res.json();
console.log(success.name);
```

**Python**

```python
import os
import requests

res = requests.get(
    "https://api.chikaraintel.com/v1/organisations/3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01",
    headers={"Authorization": f"Bearer {os.environ['CHIKARA_API_TOKEN']}"},
    timeout=30,
)

if not res.ok:
    body = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
    message = "; ".join(e.get("message", "") for e in body.get("errors", [])) or res.reason
    raise RuntimeError(f"{res.status_code}: {message} (correlation {body.get('correlation_id')})")

print(res.json()["success"]["name"])
```

## MCP tool errors

MCP tools report a refused call as a tool result with `isError: true` and a message the assistant can act on, such as `Not found.` or a condition that failed validation. Only a fault on our side surfaces as a JSON-RPC error. Each [tool page](/docs/mcp/tools) lists the messages it returns.

## Related

- https://chikaraintel.com/docs/api/pagination.md
- https://chikaraintel.com/docs/authentication.md
- https://chikaraintel.com/docs/limits.md
