# Errors

The API uses HTTP status codes and returns the same JSON body for every error. Read the `name` in your code and show the `message` to people.

## Error Body

```json JSON
{
  "name": "validation_error",
  "status": 400,
  "message": "Either html or text is required",
  "errors": [
    { "message": "Either html or text is required", "location": "html" }
  ],
  "request_id": "…"
}
```

A validation error lists each problem in `errors`. The location is a dot path to the field, such as `attachments.0.filename`.

## Status Codes

| Status | Name                          | Cause                                                     |
| ------ | ----------------------------- | --------------------------------------------------------- |
| 400    | validation_error, bad_request | The request is not valid.                                 |
| 401    | unauthorized                  | The API key is missing, wrong, expired or revoked.        |
| 403    | forbidden                     | The key cannot do this, or sending is paused.             |
| 404    | not_found                     | The resource does not exist.                              |
| 409    | conflict                      | The resource is in the wrong state, such as a sent email. |
| 429    | rate_limit_exceeded           | Too many requests, or the daily limit is reached.         |
| 500    | internal_server_error         | Something went wrong on our side.                         |

## Rate Limits

Each response carries `ratelimit-limit`, `ratelimit-remaining` and `ratelimit-reset`. A `429` also carries `retry-after`, the number of seconds to wait before you try again.

## Idempotency

Routes that change data take an optional `Idempotency-Key` header of 1 to 256 printable ASCII characters. A retry with the same key and request returns the first response with `Idempotent-Replayed: true`, so the email is sent only once.

```bash cURL
curl -X POST https://api.inboxjot.com/v1/emails \
  -H "Authorization: Bearer $INBOXJOT_API_KEY" \
  -H "Idempotency-Key: order-1042-receipt" \
  -H "Content-Type: application/json" \
  -d '{ … }'
```

```javascript Node.js
import { Inboxjot } from 'inboxjot';

const inboxjot = new Inboxjot();

await inboxjot.emails.send(email, { idempotencyKey: 'order-1042-receipt' });
```

```rust Rust
use inboxjot::Inboxjot;

let client = Inboxjot::from_env()?;

client
    .emails()
    .idempotency_key("order-1042-receipt")
    .send(&email)
    .await?;
```

```go Go
client := inboxjot.NewClient("")

email, err := client.Emails.Send(ctx, params, inboxjot.WithIdempotencyKey("order-1042-receipt"))
```

```python Python
from inboxjot import Inboxjot

inboxjot = Inboxjot()

inboxjot.emails.send(email, idempotency_key="order-1042-receipt")
```

| Status | Name                           | Cause                                                           |
| ------ | ------------------------------ | --------------------------------------------------------------- |
| 400    | invalid_idempotency_key        | The key is empty, too long or not printable ASCII.              |
| 409    | concurrent_idempotent_requests | A request with this key is still running. Retry after 1 second. |
| 422    | invalid_idempotent_request     | The key was already used with a different request.              |

Keys last 24 hours. Only a 2xx response is stored, so after an error you can fix the request and retry with the same key.
