Deliveries
Follow one event to one destination, read its attempt history, and retry it when it fails.
A delivery is one event sent to one destination through one connection, with a full record of every attempt. If an event matches three connections it produces three deliveries, each with its own status and history.
Delivery IDs start with dlv_ and attempt IDs with att_. The delivery is the billable unit. See Plans.
How it works
A delivery is created pending when an event is routed. A worker sends it to the destination, records the attempt, and moves the delivery to a new status. If the attempt fails, the delivery is retried on a schedule until it succeeds or the schedule runs out. The delivery row keeps only the latest attempt's response, so the attempt list is the only record of earlier attempts.
Statuses
| Status | Meaning |
|---|---|
pending | Queued, awaiting its next attempt. |
throttled | Held back and queued. See throttledReason. |
success | The destination answered with a 2xx. |
failed | The latest attempt failed. Check nextAttemptAt: a date means it will be retried, null means the schedule is spent. |
skipped | Terminal, nothing sent. The route was disabled: the destination, its connector or the connection. |
filtered | Terminal, nothing sent. The event type is no longer in the connection's allowlist. |
pending and throttled are transient. success and failed come from an attempt that was actually made. skipped and filtered are deliberate non-sends and are not failures.
Throttled
A throttled delivery is queued, not lost, and waiting consumes no retry attempt. throttledReason says why:
throttledReason | Cause |
|---|---|
rate_limit | SolidHooks is pacing delivery: your plan's throughput or the destination's own rateLimit. |
destination_busy | The destination answered 429, 502 or 504. SolidHooks honours its Retry-After. |
throttledAt is set the first time a delivery is held and never cleared, so a delivery that was held and then succeeded still shows it. It is the historical record, while status is the current state. throttledReason is null when the delivery is not parked.
Fields
| Field | Notes |
|---|---|
eventId, sourceId, destinationId, connectionId | What the delivery came from and went to. connectionId can be null if the connection was deleted. |
replayId | The replay that created this delivery, or null for the original fan-out. |
status | One of the statuses above. |
attempts | Number of attempts made. |
lastAttemptAt | Time of the most recent attempt. |
nextAttemptAt | When the next attempt is due. null means no further attempts are scheduled: finished, or the retry plan is spent. |
throttledAt, throttledReason | See above. |
responseStatus, responseBody, responseHeaders | The latest attempt's response. |
requestBody, requestHeaders | What was sent. |
error | Why the latest attempt failed, if it did. |
createdAt, updatedAt | Timestamps. |
Attempts
GET /v1/deliveries/{deliveryId}/attempts lists every attempt for a delivery, oldest first, with items, nextCursor and hasMore. Pass cursor and limit (1 to 100, default 20) to page.
curl -s "$API/v1/deliveries/dlv_.../attempts" \
-H "authorization: Bearer $SDHK_KEY"Each attempt records:
| Field | Notes |
|---|---|
attemptNumber | 1-based. Not unique: a throttled attempt consumes no retry budget and shares its number with the attempt that follows it. |
status | pending, success, failed, throttled, skipped, filtered or interrupted. |
trigger | initial, auto_retry or manual_retry. Only the first attempt of a manually retried delivery is manual_retry. |
startedAt, finishedAt | finishedAt is null while in flight. |
responseStatus, responseTimeMs | What the destination returned and how long it took. |
responseHeaders | Sensitive values redacted. |
responseBody | Truncated to 4 KB. |
error | Why the attempt failed. |
nextRetryAt | When the next attempt was scheduled. null on the final attempt, which separates a delivery still retrying from one that has given up. |
actorType, actorId, actorEmail | Who asked for a manual retry: user, api_key or agent. Unset on every other attempt. |
Order by startedAt, not by attemptNumber. interrupted is not a destination failure: it means no outcome was recorded because the worker stopped, and a resumed execution can replace it with its real outcome. Deliveries created before attempt history was recorded return no attempts.
In the dashboard, open a delivery to see Overview, Request, Response and Timeline tabs. The timeline shows each attempt, including parked and not-sent ones, and who retried it.
Retry schedule
A failed delivery is retried automatically on an exponential schedule, and the number of attempts and the time window depend on your plan. Retries are free: they reuse the same delivery. The full schedule, Retry-After handling and how responses are classified are on Retries.
Retry a delivery
POST /v1/deliveries/{id}/retry re-queues a single delivery. It creates no new delivery and is not billed.
curl -s -X POST "$API/v1/deliveries/dlv_.../retry" \
-H "authorization: Bearer $SDHK_KEY"{ "ok": true }A delivery that still has a live job returns 409 CONFLICT. For a throttled delivery the message says it is queued behind a rate limit and will send automatically, so wait instead of retrying. A delivery already scheduled for its next attempt is also refused.
Retry resends one delivery to one destination. To run an event through all of its connections again, replay the event. A replay creates new deliveries and is billed.
List deliveries
GET /v1/deliveries returns a page of deliveries, newest first by default.
| Parameter | Filters by |
|---|---|
eventId | Deliveries of one event. |
sourceId | Deliveries from one source. |
destinationId | Deliveries to one destination. |
connectionId | Deliveries through one connection. |
status | One of the statuses above. |
from, to | Created on or after, or on or before, a date. |
cursor, limit | Paging. limit is 1 to 100, default 20. |
search, sortBy, sortDir | Search term and sort. sortDir is asc or desc. |
curl -s "$API/v1/deliveries?status=failed&destinationId=dest_...&limit=50" \
-H "authorization: Bearer $SDHK_KEY"The response is { items, nextCursor, hasMore }. Pass nextCursor back as cursor for the next page.
In the dashboard, Deliveries filters by status, source, destination and connection.
Queue snapshot
GET /v1/deliveries/stats returns what is queued right now:
{ "pending": 12, "throttled": 3 }pending is deliveries moving and awaiting their next attempt. throttled is deliveries held by a rate limit. This is a live snapshot, not lifetime totals. The dashboard shows it as queued and rate-limited counts on the Deliveries page.
For live updates without polling, GET /v1/deliveries/stream tails delivery activity as Server-Sent Events. See Streams.
What the destination receives
Every delivery request carries x-source-id, x-event-id, x-organization-id, x-destination-id and x-delivery-id, your configured headers, and the signature headers for the destination's scheme. See Delivery signatures for how to verify them.
Next steps
- Retries — the schedule and how responses are interpreted
- Replay — re-run an event through its connections
- Destinations — rate limits and timeouts that shape delivery
- Delivery signatures — verify what you receive
- Streams — tail deliveries live
API reference: Deliveries.