Skip to main content
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

AttemptDelay before next try
1 (initial)
21 minute
35 minutes
430 minutes
5+Marked DEAD
Total window before dead-lettering: ~36 minutes.
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.

Failure classes

Different failure types produce different audit-log reason codes:
Failure classExamplesReason code
Auth failureHTTP 401, 403; S3 AccessDenied; SFTP Authentication failedREPEATED_AUTH_FAILURE
Server errorHTTP 5xx; S3 InternalError; SFTP I/O errorREPEATED_5XX
Network errorDNS failure, connection refused, TLS error, timeoutREPEATED_NETWORK_FAILURE
Host key mismatchSFTP HOST_KEY_MISMATCHREPEATED_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:
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 (POST /api/v1/delivery-destinations/{id}/test). A successful test auto-reactivates a disabled destination.

Response handling (webhooks)

ResponseOutcome
2xxSUCCESS, no further attempts
3xxTreated as failure — webhooks must respond inline, not redirect
4xxFailure, retried
5xxFailure, 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:
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:
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.