> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.superpath.io/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Learn how to handle and recover from errors received from the SuperPath API

# Learn how to handle and recover from errors received from the SuperPath API

**In this article:**
* Overview
* Success status codes
* Error status codes
* What an error response body looks like
* Handling errors in your integration
* Things to keep in mind
* FAQs

## Overview

Every integration has to deal with errors sooner or later — an expired token, a record that has since been deleted, a payload that fails validation, or a transient problem on our side. The SuperPath API signals all of these through the **HTTP status code**, so the status code is what your integration should branch on.

The response body carries the human-readable detail. It is deliberately kept simple, and its shape varies a little depending on which error you hit, so read it defensively rather than assuming a single fixed envelope. There is more on this in "What an error response body looks like" below.

## Success status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| `200 OK` | The request succeeded and the response body contains the data you asked for. | Read the body. |
| `201 Created` | The record was created. The body contains the unique ID of the new record. | Store the returned ID — you will need it for later updates. |
| `204 No Content` | The request succeeded and there is nothing to return, which is typical of updates and deletes. | Do not try to parse a body; there is not one. |
| `207 Multi-Status` | A request that performed several actions where some succeeded and some failed. | Inspect the body to see which individual items failed, rather than treating the whole call as a success. |

## Error status codes

The following HTTP status codes are returned from the SuperPath API.

![](https://storage.crisp.chat/users/helpdesk/website/c0645e580ffc0800/image_9v7jx.png)

| Status | Meaning | What to do |
| --- | --- | --- |
| `400 Bad Request` | The request could not be understood or failed validation. This is what you get when a required field is missing, a field is the wrong type, or a bearer token is supplied in the wrong format. | Fix the request. The body names the specific field or problem. Do not retry unchanged. |
| `401 Unauthorized` | Authentication or permission failed. This covers a missing or invalid token, a disabled user, a user who has no access to the organisation being requested — **and** a permission denial, where the credential is valid but the user's role is not allowed to perform that action on that record. | Check the API key and the role of the user it was created against. Do not retry unchanged, since retrying will not grant the permission. |
| `403 Forbidden` | The action is not permitted. | Treat this like a `401` — a permission or eligibility problem to be resolved rather than retried. |
| `404 Not Found` | The requested record does not exist, or the ID is wrong. It is also what you get when the API key itself cannot be found, so a `404` on a request you expect to work is worth checking against your key. | Confirm the ID, and confirm the record has not been deleted. Do not retry unchanged. |
| `409 Conflict` | The record already exists. This usually means a duplicate create — for example, a user with that email address is already in the organisation. | Look the existing record up and update it instead of creating it again. |
| `422 Unprocessable Entity` | The request was understood but cannot be processed as sent — most often a required identifier was not specified. | Correct the request. Do not retry unchanged. |
| `423 Locked` | The organisation is temporarily locked because it is being migrated between regions, so writes are not accepted. This response carries the machine-readable code `TENANT_MIGRATING`. | Pause and retry later. Migrations are planned and time-limited. Branch on the `TENANT_MIGRATING` code if you want to show your users a specific message. |
| `429` | You have exceeded a rate limit or usage allowance. | Back off and retry. See [Learn about API rate limits and how to work with them](https://help.superpath.io/en/article/learn-about-api-rate-limits-and-how-to-work-with-them-1dr37nb/). |
| `498` | A non-standard code meaning the authentication token has expired. | Obtain a fresh token, then retry the request once. |
| `500 Internal Server Error` | Something went wrong on our side. | Retry with backoff. If it persists, contact support with the timestamp and the request details. |
| `502 Bad Gateway` | A service SuperPath depends on failed or returned something unusable. | Retry with backoff. |
| `503 Service Unavailable` | A service is temporarily unavailable. | Retry with backoff. |

> **Note:** SuperPath returns `401` for permission denials as well as for authentication failures, so do not read a `401` as "my API key is wrong" without also checking the role of the user the key belongs to. A `403` is used in a smaller number of cases, so handle both.

## What an error response body looks like

Most error responses have a **plain-text** body containing just the message. This is what the [API reference](https://superpath.readme.io/reference) declares for its `400`, `401` and `404` responses, and it is what you should expect by default:

```http
HTTP/1.1 404 Not Found

User not found: auth id 8f21c0d4-4a1e-4f2b-9c11-6b7f8a0d3e55
```

A validation failure returns the specific problem in the same way, which makes `400` responses genuinely useful for debugging:

```http
HTTP/1.1 400 Bad Request

"firstName" is required
```

Some responses are a JSON object instead. The generic authentication failure is one of these — a `message` key and nothing else:

```json
{
  "message": "You are not authorised to perform this action"
}
```

Where SuperPath has a machine-readable code for the condition, the body carries a `code` alongside the `message`, so your integration can branch on the code rather than matching on message text. `TENANT_MIGRATING` on a `423` response is currently the only such code:

```json
{
  "message": "...",
  "code": "TENANT_MIGRATING"
}
```

Rate limit rejections use an `error` key rather than `message`:

```json
{
  "error": "Too many requests. Please try again later."
}
```

> **Tip:** Because the body may be plain text or JSON, and JSON bodies may use `message` or `error`, parse it defensively. A helper that tries JSON, falls back to the raw string, and then reads whichever of `message` or `error` is present will keep your error logs readable across every case.

## Handling errors in your integration

1. **Branch on the status code first.** It is the only part of the response with a stable, documented contract.
2. **Retry only what is worth retrying.** Retry `429`, `500`, `502` and `503` with exponential backoff and jitter. Retry `498` once, after refreshing your token. Retry `423` later, on a longer delay, because a region migration takes longer than a normal transient blip.
3. **Never blind-retry a `400`, `401`, `403`, `404`, `409` or `422`.** These describe something about the request itself, so the same request will fail the same way. Log it and surface it.
4. **Log the status code and the body together.** The body carries the field name or record ID that explains the failure, and without it a `400` or `404` in your logs tells you almost nothing.
5. **Cap your retries.** An uncapped retry loop turns a transient error into sustained load, and can get you rate limited on top of the original problem.
6. **Be careful about retrying writes.** A `500` on a create is ambiguous — the record may or may not have been written. Where you can, look the record up before creating it again, or send a stable identifier of your own so duplicates are easy to spot.

## Things to keep in mind

* The status code is the contract; the message text is not. Many messages are translated, and any of them can be reworded, so never match on the exact string to make a decision.
* For the same reason, the wording you see in your own tests may differ from what your customer's organisation receives.
* `404` has two quite different meanings — the record was not found, or your API key was not found. Rule the key out before assuming the record is missing.
* A `401` is not always about credentials. If the same key works for other calls, the likely cause is that the user the key belongs to does not have permission for that specific action or record.
* Keep your API key safe. Once created it cannot be viewed again — see [Learn how to authenticate with the SuperPath API](https://help.superpath.io/en/article/learn-how-to-authenticate-with-the-superpath-api-5xo64k/).
* If you hit an error you cannot explain, contact [support@superpath.io](mailto:support@superpath.io) with the endpoint, the timestamp, the status code and the response body.

## FAQs

**Is there a machine-readable error code on every response?**
No. Codes are added only where a client needs to branch on a specific condition, and `TENANT_MIGRATING` on a `423` is currently the only one. For everything else, branch on the HTTP status code.

**Why am I getting a `401` when my API key definitely works?**
Because permission denials also return `401`. The key is authenticating fine, but the user it was created against does not have the role permission needed for that action or that record. API keys inherit the permissions of the user who created them.

**Why am I getting a `404` for a record I know exists?**
Check the ID and the organisation first, then check the key itself — an API key that cannot be found also produces a `404`. It is also worth confirming the record has not been deleted or archived since you cached its ID.

**Which errors are safe to retry?**
`429`, `500`, `502` and `503` with backoff; `498` once you have refreshed the token; `423` after a longer wait. Everything else needs the request to change before it will succeed.

**Should I parse the error body as JSON?**
Try to, but do not depend on it. Most error bodies are plain text, so your parser needs to fall back to treating the body as a string.
