How Delivery Works
Push extracted, resolved, and reviewed data to any downstream system. Delivery is a typed, at-least-once pipeline with idempotency keys on the wire, append-only history, and a dead-letter queue for terminal failures. The system is fully configurable without code changes — signals, deliverable resolvers, serializers, and connectors are four orthogonal registries that compose independently, so adding a new destination type never requires changes to the signal or serializer code.
Every delivery flows through a five-stage pipeline. Producers are stateless — they only publish typed events into an outbox and never interact with destinations or bindings directly. A background poller drains the outbox every 5 seconds (configurable via delivery.poll_interval_ms), claiming up to 50 rows per tick using FOR UPDATE SKIP LOCKED for safe multi-instance operation. When the BullMQ queue depth exceeds the backpressure threshold (default 10,000), the poller pauses until the queue drains, preventing memory exhaustion under burst load. Matched events are enqueued as delivery jobs processed by workers (default concurrency: 10):
The delivery pipeline
| Parameter | Type | Description |
|---|---|---|
| 1. Signal | event | A producer emits a typed event (e.g. document.extracted, result.approved) into the outbox. Producers are stateless — they only publish. |
| 2. Binding | match | A poller drains the outbox and matches each event against active bindings. A binding joins a signal filter to a deliverable + destination + serializer. |
| 3. Resolver | load | The deliverable resolver loads the payload (document metadata, a record snapshot, an extraction run, ...) at delivery time using only entity IDs from the signal. |
| 4. Serializer | encode | The serializer encodes the payload into the wire format — json, ndjson, csv, csv_file, xlsx, rows, graph, raw, md, or txt — after an optional field_map projection. |
| 5. Connector | transport | The connector ships the encoded bytes through the TransportWrapper (SSRF guard, payload cap, rate limit, retry ladder). Slice-1 connector is webhook. |
Every attempt is logged in delivery_items. Terminal failures (retry exhausted or permanent 4xx) write a delivery_dead_letter row, which is replayable. The outbox, history, DLQ, and catalog are all accessible via the `/v1/delivery/*` API.
The four registries — signals, deliverables, serializers, and connectors — are fully orthogonal. Adding a new destination type does not require changes to the signal or serializer code. This composable design means you can mix any supported signal with any compatible serializer and connector without custom integration work.
For best results, start with a webhook destination to verify your binding configuration end-to-end. Once the payload shape and delivery cadence match your expectations, expand to file-based destinations (S3, SFTP) or spreadsheet destinations (Google Sheets). Most teams create separate bindings for different downstream consumers rather than routing all events to a single destination.
# 1) Create the destination
curl -X POST https://api.talonic.com/v1/delivery/destinations \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ops Webhook",
"type": "webhook",
"config": { "url": "https://ops.example.com/talonic", "method": "POST" },
"signing_secret": "whsec_rotate_monthly"
}'
# -> { "id": "dest_001", "is_active": true }
# 2) Create a binding
curl -X POST https://api.talonic.com/v1/delivery/bindings \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Notify on extraction",
"signal_filter": { "event_type": "document.extracted" },
"deliverable_type": "notification",
"destination_id": "dest_001",
"serializer_format": "json"
}'
# 3) Test the destination
curl -X POST https://api.talonic.com/v1/delivery/destinations/dest_001/test \
-H "Authorization: Bearer $TALONIC_API_KEY"
# -> { "success": true, "duration_ms": 142 }The delivery pipeline is designed for zero-code configuration. Adding a new destination, creating a binding, and testing the connection can all be done via the API or dashboard without writing any integration code. The compatibility triangle validation ensures that your binding configuration is valid before any events are routed — you never end up with a misconfigured binding that silently drops deliveries. Every attempt is logged with full request/response details, and terminal failures are captured in the dead-letter queue for replay, giving you complete visibility into your delivery pipeline.
X-Talonic-Idempotency-Key header (or equivalent metadata for file-based connectors) to deduplicate on their end.