Delivery Guarantees

Carlquist is designed for reliable delivery across unreliable legacy systems and networks. We provide at-least-once delivery with idempotency controls, configurable retries, and dead-letter handling so integrations can be made deterministic and debuggable.

Delivery Semantics

Carlquist provides at-least-once delivery. Every event that enters the pipeline will be delivered to the configured target at least once. Duplicate delivery is possible during retries; use idempotency keys to make processing effectively-once.

Idempotency

Every event carries an X-Carlquist-Idempotency-Key header containing a UUID. The key is scoped per-stream, per-target. Consumers should use this key to deduplicate events on their end.

Carlquist tracks delivery acknowledgment per idempotency key. If the target returns a 2xx response, the event is marked as delivered and will not be retried.

POST /hooks/orders HTTP/1.1
Host: your-app.com
Content-Type: application/json
X-Carlquist-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
X-Carlquist-Stream: orders.created
X-Request-Id: req_7f3a9b2c

{ "order_id": "ORD-12345", "status": "created", ... }

Retry Policy

The default retry policy uses exponential backoff with jitter. The schedule progresses as follows:

Attempt 1:  1 second
Attempt 2:  5 seconds
Attempt 3:  30 seconds
Attempt 4:  2 minutes
Attempt 5:  10 minutes
Attempt 6:  1 hour
Attempt 7:  6 hours

Total window: ~7.5 hours across 7 attempts

Retry behavior is configurable per stream:

carlquist stream update orders.created \
  --max-retries 10 \
  --initial-delay 2s \
  --max-delay 30m \
  --backoff-multiplier 3

Non-Retryable Responses

Responses with 4xx status codes (except 429 Too Many Requests) are considered permanent failures. They are not retried and go directly to the dead-letter queue with the response body attached for debugging.

Dead-Letter Queue (DLQ)

Events that exhaust all retry attempts are moved to the dead-letter queue. Each DLQ entry includes:

Retention

DLQ entries are retained for 7 days by default. Enterprise plans support extended retention: 14, 30, or 90 days.

Replay

Individual events or bulk batches can be replayed from the DLQ via the CLI or dashboard:

# Replay a single event
carlquist dlq replay --stream orders --id evt_xxx

# Replay all failed events from a time range
carlquist dlq replay --stream orders \
  --from 2026-02-20 --to 2026-02-25

# Replay with a filter
carlquist dlq replay --stream orders \
  --status-code 503

Monitoring

DLQ depth is exposed as a Prometheus-compatible metric:

carlquist_dlq_depth{stream="orders.created"} 12

Backpressure

If the target returns 429 Too Many Requests or is consistently slow (p99 latency exceeding 10 seconds), Carlquist automatically reduces the delivery rate. Rate recovery follows an AIMD (Additive Increase / Multiplicative Decrease) algorithm — rate drops quickly on overload and recovers gradually.

Backpressure events are logged and visible in the dashboard under the stream's health panel.

Ordering

Events from a single adapter are delivered in order within a single stream. Cross-stream ordering is not guaranteed. If strict ordering is required across multiple event types, route them through a single stream per logical sequence.

Billing

Only successful deliveries (2xx response from target) count as billable events. The following are not billed:

Fanout billing: If one source event is delivered to N targets (fanout), it counts as N billable events. For example, an order.created event delivered to 3 webhooks counts as 3 billable events.