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

# Retry and delivery semantics

> How the delivery pipeline behaves across all destination types: scheduling, retries, failure classes, dead-lettering, and auto-disable.

Deliveries are processed by an asynchronous queue. Your application code never blocks on a delivery — Mapping.Travel enqueues a delivery row in the same database transaction that emits the event, and a background worker drains the queue.

This behaviour is identical for all three destination types (Webhook, S3, SFTP).

## Lifecycle

```
job completes / event fires
        │
        ▼
PENDING ──── success ────▶ SUCCESS
    │
    │ failure (any)
    ▼
PENDING (attempt 2, retry after 1m)
    │
    ▼
PENDING (attempt 3, retry after 5m)
    │
    ▼
PENDING (attempt 4, retry after 30m)
    │
    ▼
DEAD (audit-logged, no more attempts)
```

## Retry schedule

| Attempt     | Delay before next try |
| ----------- | --------------------- |
| 1 (initial) | —                     |
| 2           | 1 minute              |
| 3           | 5 minutes             |
| 4           | 30 minutes            |
| 5+          | Marked `DEAD`         |

Total window before dead-lettering: \~36 minutes.

<Note>
  Mapping.Travel does not implement infinite retries. If your endpoint or bucket is unavailable for hours, dropping events is preferable to building an unbounded backlog.
</Note>

## Failure classes

Different failure types produce different audit-log reason codes:

| Failure class     | Examples                                                       | Reason code                |
| ----------------- | -------------------------------------------------------------- | -------------------------- |
| Auth failure      | HTTP 401, 403; S3 `AccessDenied`; SFTP `Authentication failed` | `REPEATED_AUTH_FAILURE`    |
| Server error      | HTTP 5xx; S3 `InternalError`; SFTP I/O error                   | `REPEATED_5XX`             |
| Network error     | DNS failure, connection refused, TLS error, timeout            | `REPEATED_NETWORK_FAILURE` |
| Host key mismatch | SFTP `HOST_KEY_MISMATCH`                                       | `REPEATED_NETWORK_FAILURE` |

After 4 failed attempts, the delivery is marked `DEAD` and the reason code is written to the audit log.

## Auto-disable

If a destination accumulates **5 dead deliveries within any 24-hour window**, it is automatically set to `INACTIVE`. No further deliveries are attempted until you re-enable it.

To re-enable a destination:

```bash theme={null} theme={null}
curl -X PATCH \
  https://api.mapping.travel/api/v1/delivery-destinations/{id} \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '{ "status": "ACTIVE" }'
```

Alternatively, run the [connection test](/delivery-destinations/testing) (`POST /api/v1/delivery-destinations/{id}/test`). A successful test auto-reactivates a disabled destination.

## Response handling (webhooks)

| Response                                        | Outcome                                                         |
| ----------------------------------------------- | --------------------------------------------------------------- |
| `2xx`                                           | `SUCCESS`, no further attempts                                  |
| `3xx`                                           | Treated as failure — webhooks must respond inline, not redirect |
| `4xx`                                           | Failure, retried                                                |
| `5xx`                                           | Failure, retried                                                |
| Connection refused / DNS error / timeout (>30s) | Failure, retried                                                |

## Idempotency (webhooks)

Every event has a stable `eventId` field reused across every retry. The delivery `id` changes per attempt.

Use `eventId` as the dedup key:

```ts theme={null} theme={null}
async function handle(payload: WebhookPayload) {
  const seen = await db.exists(`webhook:${payload.eventId}`);
  if (seen) return; // already processed
  await processEvent(payload);
  await db.set(`webhook:${payload.eventId}`, '1', { ex: 86400 });
}
```

## Idempotency (S3 and SFTP)

S3 and SFTP deliveries write a file with a deterministic name. If a delivery is retried, the file is overwritten with identical content — the result is the same whether one or three attempts succeed.

## Debugging

List recent delivery attempts for a destination:

```bash theme={null} theme={null}
curl "https://api.mapping.travel/api/v1/delivery-destinations/{id}/deliveries?limit=50" \
  -H "Authorization: Bearer <your-token>"
```

Each entry includes `status`, `attemptCount`, `lastError`, and timestamps. In the app, open **Developers → Delivery destinations → \[destination name] → Deliveries**.

## Payload size limit (webhooks)

Webhook payloads are capped at 64 KB. If a single event's data exceeds the cap, the `data` field is replaced with `{ "truncated": true, "dataRef": "delivery/<id>" }`. Query the API for the full state if you encounter this.

## Backpressure

If an organization accumulates more than 10,000 `PENDING` deliveries, new deliveries are dropped silently and an hourly audit-log entry records the back-pressure event. Once the queue drains below the threshold, normal delivery resumes.
