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

# Errors

> Error response shape, status codes, typed SDK errors, request IDs, and which failures are safe to retry.

Every failed request returns a JSON body with a `status` and a `detail`:

```json theme={null}
{
  "status": 400,
  "detail": "Validation failed: name is required"
}
```

`status` repeats the HTTP status code. `detail` is a human-readable explanation intended for logs and developers — it is not a stable identifier, so **do not branch on the text of `detail`**. Branch on the HTTP status, or on the typed error the SDK raises.

***

## Status codes

| Status | Meaning | What to do |
| - | - | - |
| `400` | Invalid parameters | Fix the request. Retrying unchanged will fail again. |
| `401` | Missing or invalid API key | Check the `Authorization` header and that the key belongs to the environment you are calling. |
| `403` | The key is valid but not allowed to do this, or the feature is not enabled for the account | Check the key's permissions and the account's plan. Retrying unchanged will fail again. |
| `404` | Resource does not exist | Check the id. Note that a resource in another environment reads as missing. |
| `409` | A request with this idempotency key is still being processed. The response has a `Retry-After` header. | Wait `Retry-After` seconds, then retry with the **same** key. This is safe. See [Idempotency](https://alguna.com/docs/api-reference/v2/idempotency.md). |
| `422` | Validation error — well-formed request, but it cannot be processed | Fix the request. Retrying unchanged will fail again. |
| `429` | The credit gate is busy, or the credit ledger has too many open holds. Only `POST /credits/check` and `POST /credits/track` send it. | Back off and retry. See [Retrying](#retrying) below. |
| `500` | Internal error | The SDKs do not retry it. Read [Retrying](#retrying) before you retry it yourself. |
| `502` | The gateway could not reach the API. The body may be empty or not JSON. | Retry with backoff. |
| `503` | Temporarily unavailable | Retry with backoff. |
| `504` | The request took too long to complete | Retry with backoff. Use an idempotency key for writes, because the write may have been applied. |

> [!NOTE]
>
> `400` and `422` both indicate the request will not succeed as sent. Broadly, `400` means the request itself was malformed, and `422` means it parsed correctly but violates a rule — for example a value that is well-formed but not valid for the current state of the resource.

***

## Request IDs

Every response carries an `X-Request-Id` header — a UUID generated per request. Log it.

When you contact support about a failed request, the request ID is the fastest way for us to find it — far quicker than a timestamp and an endpoint. The TypeScript SDK surfaces it as `requestId` on every error from a response.

```bash theme={null}
curl -i https://api.alguna.io/customers \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -H "Alguna-Version: 2026-04-01"
# ...
# X-Request-Id: 9f1c2e7a-4b83-4d6e-9a51-2c0f7b3d8e14
```

***

## Typed errors in the TypeScript SDK

The [TypeScript SDK](https://alguna.com/docs/api-reference/v2/sdks/typescript.md) maps failed responses onto typed error classes, so you can branch with `instanceof` rather than inspecting status codes by hand.

| Class | Status |
| - | - |
| `ValidationError` | `400` |
| `UnprocessableEntityError` | `422` (extends `ValidationError`) |
| `AuthenticationError` | `401` |
| `PermissionDeniedError` | `403` |
| `NotFoundError` | `404` |
| `ConflictError` | `409` |
| `RateLimitError` | `429` |
| `InternalServerError` | any `5xx` |
| `ApiError` | any other status |
| `ConnectionError` | no response arrived (network failure) |
| `TimeoutError` | the request timed out (extends `ConnectionError`) |
| `AbortError` | you aborted the request with an `AbortSignal` |

All of them extend `AlgunaError` and carry:

* `message` — the body's `detail`, else `title`, else `message`, else the HTTP status text
* `statusCode` — the HTTP status (`undefined` when no response arrived)
* `requestId` — the `X-Request-Id` of the failed response
* `code` — set when the body carries one (a `401` from the gateway sends `unauthenticated`)
* `body` — the parsed response body
* `headers` — the response headers, lower-cased (for example `retry-after` on a `429`)

```typescript theme={null}
import { AlgunaError, NotFoundError, ValidationError } from "@alguna/sdk";

try {
  await alguna.customers.create({ name: "Acme Corp", currency: "USD" });
} catch (error) {
  if (error instanceof ValidationError) {
    // error.message — e.g. "currency: must be a supported currency"
    return reportInvalidInput(error.message);
  }
  if (error instanceof NotFoundError) {
    return handleMissing();
  }
  if (error instanceof AlgunaError) {
    // Unexpected: log the request id so support can trace it.
    logger.error({ requestId: error.requestId, status: error.statusCode });
  }
  throw error;
}
```

> [!NOTE]
>
> `UnprocessableEntityError` extends `ValidationError`, so one `instanceof ValidationError` check handles both `400` and `422`.

Only `credits.check` and `credits.track` throw `RateLimitError`: the credit gate stayed busy, or the ledger stayed at its limit of open holds, through every retry. Insufficient credit is not an error. Those calls return it as `allowed: false`.

Before it throws, the SDK retries a failed attempt twice: on `408`, `429`, `502`, `503` and `504`, on a `409` with `Retry-After`, and when no response arrived (a refused or reset connection, or a timed-out attempt). It waits with exponential backoff, or for `Retry-After` when the response has one, at most 60 s. Every request that is not a GET carries an `Idempotency-Key`, yours or a generated one, that its retries reuse, so the SDK never applies a write twice. `events.ingest`, `credits.track` and `credits.check` are the exceptions. The events in the request body identify the work, so the SDK sends no generated key for these calls and retries them without one. It still sends a key that you pass. See [Writes identified by their events](https://alguna.com/docs/api-reference/v2/idempotency.md#writes-identified-by-their-events). Set `maxRetries: 0` on the client to turn retries off.

***

## Retrying

Retry these:

* **`429`** — back off exponentially before retrying.
* **`502`, `503` and `504`** — retry with exponential backoff and jitter, with the **same** idempotency key.
* **`409` with `Retry-After`** — the earlier request with that idempotency key is still in flight. Wait `Retry-After` seconds, then retry with the **same** key. This is safe. Do not generate a new key, or you risk creating a duplicate.
* **Network timeouts** — you do not know whether the request was applied. Retry with the same idempotency key.

Do not retry `400`, `401`, `403`, `404` or `422` without changing the request. They are deterministic and will fail identically.

A `500` is an unexpected error inside the API, and the request may have changed data before it failed. The API stores no result for a `5xx` under the idempotency key, so a retry runs the request again: it does not replay a result. For this reason the SDKs do not retry a `500`. If you retry one yourself, retry only a request that is safe to run twice, and send the **same** idempotency key.

> [!WARNING]
>
> Always send an `Idempotency-Key` on writes you might retry. Without one, a retry after a timeout can create a second customer, subscription or invoice. `POST /events` and `POST /credits/track` are the exceptions: the events' `unique_id`s identify the work. See [Idempotency](https://alguna.com/docs/api-reference/v2/idempotency.md).

***

## Related

> **[Idempotency](https://alguna.com/docs/api-reference/v2/idempotency.md)**
>
> Idempotency keys, replay behaviour, and the `409` conflict case.

> **[TypeScript SDK](https://alguna.com/docs/api-reference/v2/sdks/typescript.md)**
>
> Installation, authentication, and error handling.


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