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

# Errors

> The shape of an error, every code the API answers with, and what to do about each one.

Every error of the API answers in JSON with three fields at least:

```json 422 Unprocessable Content theme={null}
{
    "message": "Not sent: ada@example.com is on the suppression list.",
    "code": "recipients_suppressed",
    "doc_url": "https://docs.lekalao.com/errors#recipients_suppressed",
    "data": { "id": "e3b0c442-98fc-4c14-9a1e-2f6b7d8a9c10", "status": "failed", "failure_code": "recipients_suppressed" }
}
```

| Field | |
| - | - |
| `message` | A sentence for a person, in the installation's language. It may be reworded: show it, do not test it. |
| `code` | What went wrong, in one word that does not change. **Test this one.** Every code is below. |
| `doc_url` | This page, at the code's entry. Under `/fr/` when the installation speaks French. |

Some errors add fields of their own: `errors` for a validation error, `problems` for a campaign that is not ready, `data` for a transactional e-mail that was logged but not sent.

## The HTTP statuses

| Status | Means | What to do |
| - | - | - |
| `200` `201` | Done, or created. | |
| `202` | Accepted and queued: the work happens in the background (an import, a batch of more than 100, a transactional e-mail). | Follow the `url` in the answer, the [webhooks](https://docs.lekalao.com/developers/webhooks.md), or read the e-mail again by its `id`. |
| `204` | Deleted. No body. | |
| `400` | Malformed request: an idempotency key longer than 255 characters. | Fix the call. |
| `401` | No token, or a revoked one. | Check the `Authorization` header. |
| `403` | The token, its owner's role, the team or a test key does not allow it. | Read `code`. |
| `404` | Nothing of that id in this team. | Check the `id`. |
| `409` | Conflict: a campaign being sent, someone already in the automation, an idempotent call still running. | Read `code`; do not send it again as is. |
| `422` | The data, or the operation, was refused. | Read `code`, then `errors` or `message`. |
| `429` | Too many calls, or the month's allowance is used up. | `rate_limited`: wait `Retry-After` seconds. `monthly_quota_exceeded`: wait for next month, or move up a plan. |
| `502` | A transactional e-mail the provider refused, after the retries, or the queue could not take. | Read `data.failure_reason`. |
| `5xx` | Lekalao's own problem. | Try again later, with the same `Idempotency-Key`. |

## Validation errors

`422` `validation_error`, with the detail field by field:

```json theme={null}
{
    "message": "The email field must be a valid email address.",
    "code": "validation_error",
    "doc_url": "https://docs.lekalao.com/errors#validation_error",
    "errors": {
        "email": ["The email field must be a valid email address."],
        "tags.2": ["The tags.2 field must not be greater than 255 characters."]
    }
}
```

Fields inside arrays are named by their position: `subscribers.14.email` is the 15th line of a batch.

## An address nothing would arrive at

Adding someone new checks the address first: `POST /v1/lists/{list}/subscribers`, and `POST /v1/automations/{automation}/trigger` for a person not on the list yet. A throwaway mailbox, or a domain with no mail server, answers `422` `undeliverable_address` with two more fields:

```json theme={null}
{
    "message": "ada@gmial.com cannot receive e-mail: its domain has no mail server. Did you mean ada@gmail.com?",
    "code": "undeliverable_address",
    "doc_url": "https://docs.lekalao.com/errors#undeliverable_address",
    "errors": {
        "email": ["ada@gmial.com cannot receive e-mail: its domain has no mail server. Did you mean ada@gmail.com?"]
    },
    "problem": "no_mail_server",
    "suggestion": "ada@gmail.com"
}
```

| Field | Value |
| - | - |
| `problem` | `disposable`: a throwaway mailbox. `no_mail_server`: the domain receives no e-mail. |
| `suggestion` | The address the person very likely meant, when a common domain is one or two keystrokes away. Otherwise `null`. |

Show `suggestion` to the person and let them choose: never swap it in on your own. In a [batch](https://docs.lekalao.com/developers/subscribers.md#subscribe-in-a-batch), the same address is a line with `"outcome": "failed"` and its message; in an import, a line in error.

## Retry or not

* **Retry** on `429` `rate_limited`, on `5xx` and on network errors, with a growing delay and the same `Idempotency-Key`.
* **Read first** a `502` from a transactional e-mail: `data.failure_reason` says whether sending again can work.
* **Do not retry** any other `4xx`: the same call gives the same answer. Fix it first. One exception: `409` `idempotency_key_in_use`, the same idempotent call still running; try again a moment later.

## Every code

Grouped by where they come from. Each one has its anchor: `/errors#rate_limited`.

## Any route

### `unauthenticated`

`401`. No `Authorization` header, a malformed token, or a revoked one. Check that the whole token follows `Bearer `. See [Authentication](https://docs.lekalao.com/developers/authentication.md).

### `missing_ability`

`403`. A **Read** token called a route that writes. Create a **Read and write** token for this application.

### `role_not_allowed`

`403`. The token's owner may not do this in the team: a viewer writing, a writer sending a campaign. A token acts with its owner's role, checked on every call. See [The roles](https://docs.lekalao.com/account/team.md#the-roles).

### `team_suspended`

`403`. The team is suspended: every call is refused until whoever runs the service lifts it. Contact support.

### `team_access_lost`

`403`. The token's owner is no longer a member of the team the token was made in. Create a token from an account that is.

### `two_factor_required`

`403`. The team requires two-factor authentication, and the token's owner has neither a second factor nor a passkey. Once they set one up, the token works again. See [Rules of the team](https://docs.lekalao.com/account/team.md#rules-of-the-team).

### `no_team`

`403`. The account is in no team at all. It happens to someone removed from every team.

### `forbidden`

`403`. Refused for a reason that has no code of its own. `message` says which.

### `not_found`

`404`. No route at this address, or no resource of this `id` in the token's team. A resource of another team is not found either, by design.

### `method_not_allowed`

`405`. The route exists, with another method: `GET` instead of `POST`, say.

### `conflict`

`409`. The state of the resource does not allow it. `message` says why.

### `bad_request`

`400`. The request could not be read.

### `payload_too_large`

`413`. The body is larger than the server accepts. Send fewer lines per batch, or a smaller file.

### `validation_error`

`422`. A field is missing or invalid. `errors` lists them, field by field. See [Validation errors](#validation-errors).

### `invalid_request`

`422`. Refused for a reason that has no code of its own. `message` says which.

### `rate_limited`

`429`. The token made more than its calls of the minute (120 by default). Wait the `Retry-After` seconds; `X-RateLimit-Remaining` says, on every answer, how many calls are left.

### `server_error`

`500`. Lekalao's fault. Try again later with the same `Idempotency-Key`; if it lasts, tell support the time of the call.

### `service_unavailable`

`503`. The installation is in maintenance. Try again in a few minutes.

## Idempotency

### `idempotency_key_too_long`

`400`. The `Idempotency-Key` is longer than 255 characters.

### `idempotency_key_in_use`

`409`. A call with the same key is still running. Try again a moment later: you will get its answer.

### `idempotency_key_reused`

`422`. The key was already used for another route or another body. Give each operation its own key.

### `idempotency_key_expired`

`422`. The key is more than 24 hours old. Send a new one. See [Idempotency](https://docs.lekalao.com/developers/conventions.md#idempotency).

## Test keys

### `sandbox_cannot_send`

`403`. A [test key](https://docs.lekalao.com/developers/sandbox.md) called a route that exists to write to real people: sending a campaign, putting someone into an automation, sending a confirmation e-mail again. Use a live key.

## Contacts

### `undeliverable_address`

`422`. The address cannot receive e-mail: a throwaway mailbox, or a domain without a mail server. See [An address nothing would arrive at](#an-address-nothing-would-arrive-at).

### `address_suppressed`

`422`. The address is on the team's [suppression list](https://docs.lekalao.com/contacts/suppression-list.md): it bounced for good, complained, or was blocked by hand. It cannot be subscribed again by the API.

### `subscriber_limit_reached`

`422`. The plan holds no more subscribers. Move up a plan, or clean the lists. See [Plans](https://docs.lekalao.com/account/billing.md#plans).

### `confirmation_not_needed`

`422`. `resend-confirmation` for someone who has nothing to confirm: already subscribed, or on a list without double opt-in.

### `batch_rejected`

`422`. Not one line of the batch could be saved. `data` gives the reason of each line, `meta` the counts.

### `contact_not_found`

`404`. A conversion for an address that is on none of the team's lists.

### `event_count_out_of_range`

`422`. `POST /v1/events` takes one event, or between 1 and 1,000 under `events`.

## Sending

### `sender_not_allowed`

`422`. The sender address is not on a verified sending domain of the team, where the installation requires one. The message names the domain. See [Sending domains](https://docs.lekalao.com/deliverability/domains.md).

### `template_error`

`422`. The template does not render: a tag left open, an unknown filter. The message says where.

### `content_missing`

`422`. A transactional e-mail with neither `template` nor `html`.

### `recipient_missing`

`422`. A transactional e-mail with no `to`, from a template that has no default recipients either.

### `too_many_recipients`

`422`. More than 50 addresses in one transactional e-mail, `to`, `cc` and `bcc` together. A transactional e-mail is written to someone: send one each.

### `recipients_suppressed`

`422`. Every address in `to` is on the suppression list. The e-mail is logged as `failed`, under `data`. Sending it again fails the same way.

### `monthly_quota_exceeded`

`429`. The plan's e-mails of the month are used up; each recipient of an e-mail counts, copies included. The e-mail is logged as `failed`, under `data`. Wait for the next month, or move up a plan. A campaign test refused for this reason answers `test_refused`.

### `sending_paused`

`422`. The team may not send: its paid period ended without a payment. The e-mail is logged as `failed`, under `data`. See [Billing](https://docs.lekalao.com/account/billing.md).

### `provider_error`

`502`. The provider refused the transactional e-mail on every try, for six hours, or a [test key](https://docs.lekalao.com/developers/sandbox.md) wrote to `failed@sink.lekalao.dev`. `data.failure_reason` holds the provider's last word.

### `queue_unavailable`

`502`. The e-mail was logged, but the queue could not take it. Try again in a moment with the same `Idempotency-Key`: a `502` is not kept, so the e-mail is not counted twice.

### `mail_not_kept`

`409`. A resend of a transactional e-mail whose template keeps neither the content nor the variables (one-time codes, sign-in links). Send a new e-mail.

### `campaign_not_ready`

`422`. The campaign cannot leave yet. `problems` lists what is missing, as the interface does. See [Ready or not](https://docs.lekalao.com/campaigns/sending.md#ready-or-not).

### `campaign_is_sending`

`409`. A campaign being sent cannot be deleted. Pause it in the interface first.

### `test_refused`

`422`. A campaign test that may not leave: the sender's domain is not verified (where the installation requires it), every address given is on the suppression list, the team has sent 100 test e-mails this hour, or the month's allowance is used up. `message` says which.

### `automation_not_running`

`409`. The automation is not active, or has no list. Switch it on in the interface.

### `already_in_automation`

`409`. The person is already travelling through this automation. The answer also carries `"started": false`.

## Licences

### `licence_invalid`

`422`. `POST /v1/licences/check`: the key cannot be read, or is not signed by Lekalao. The answer also carries `"valid": false, "reason": "invalid"`.

### `licence_revoked`

`403`. The licence has been revoked. The answer also carries `"valid": false, "reason": "revoked"`.

## A transactional e-mail that failed

A transactional e-mail keeps why it failed in `failure_code`, with the same words as the codes above: `recipients_suppressed`, `monthly_quota_exceeded`, `sending_paused`, `provider_error`, `queue_unavailable`. One more is never an answer to a call, since it happens later: `lost`, an e-mail still `pending` seven hours after the call because its job was lost. Send it again. See [Transactional e-mails](https://docs.lekalao.com/developers/transactional.md#the-answer).


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