Skip to main content

Webhooks

Deliveries and retries

Every dispatch is recorded as a delivery you can inspect, retry, or replay. This is how you diagnose a receiver that stopped accepting events — and how you recover the ones it missed.

On this page

Delivery lifecycle

Repeated delivery failures pause a subscription automatically; paused subscriptions dead-letter incoming events until resumed. Use the delivery, retry, and replay endpoints to inspect and recover.

A delivery moves through four states: pending while queued, delivered once the target accepts it, failed after an attempt that did not succeed, and dead_lettered once retries are exhausted or the subscription is paused.

A paused subscription still costs you events

Repeated failures pause a subscription automatically. While it is paused, matching events are dead-lettered rather than delivered — they are recoverable with a bulk replay, but only if you notice. Watch the counters below.

Inspecting deliveries

Delivery history carries the attempt count, the last HTTP response code the target returned, and the exact payload that was sent — enough to tell a receiver bug from a network problem. It is also where you read a payload that arrived truncated.

FieldTypeRequiredDescription
idstringOptionalDelivery id — a UUID, and the value sent as the X-Inkfree-Delivery header.
subscriptionIdstringOptionalSubscription this delivery belongs to.
eventIdstringOptionalId of the event being delivered.
eventTypestringOptionalEvent type, e.g. envelope.completed, or inkfree.test for test dispatches.
payloadstringOptionalRaw JSON body sent to the target URL. Read the full payload here when a delivery arrived truncated.
statusstringOptionalCurrent state of the delivery.pendingdeliveredfaileddead_lettered
attemptsintegerOptionalNumber of attempts made so far.
lastResponseCodeintegerOptionalHTTP status returned by the target on the last attempt.
nextAttemptAtstringOptionalWhen the next retry is scheduled, formatted MM/dd/yyyy HH:mm:ss.
lastAttemptAtstringOptionalWhen the last attempt was made, formatted MM/dd/yyyy HH:mm:ss.
versionstringOptionalPayload schema version.
curl
curl "https://api-uat.softsages.com/core/api/v1/inkfree/webhooks/whk_123/deliveries" \
  -H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

For a quick health check across all subscriptions, read the counters instead:

curl
curl "https://api-uat.softsages.com/core/api/v1/inkfree/webhooks/metrics" \
  -H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 response
{
  "account": "acme",
  "app": "inkfree",
  "by_subscription": {
    "whk_123": {
      "pending": 0,
      "delivered": 412,
      "failed": 3,
      "dead_lettered": 1
    }
  }
}

Recovering

Three endpoints replay events, and they differ in what payload they send. Retry and bulk replay reuse the stored payload; the arbitrary-payload replay sends exactly the body you pass, which is what you want when reproducing a specific case against a receiver under test.

A retry gets a new delivery id

Retrying creates a new delivery record rather than mutating the original, so your receiver sees an X-Inkfree-Deliveryit has not seen before. Deduplicate on the event's own id if you need to suppress the repeat — see Deduplication.

Bulk replay is the recovery path after an outage on your side. It defaults to dead_lettered deliveries from the last 24 hours, clamps since_hours to 1–720, and replays at most 500 deliveries per call — run it repeatedly to work through a larger backlog.

curl
curl -X POST "https://api-uat.softsages.com/core/api/v1/inkfree/webhooks/whk_123/replay/bulk" \
  -H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "dead_lettered",
  "since_hours": 48
}'
200 response
{
  "subscription_id": "whk_123",
  "status_filter": "dead_lettered",
  "since": "2026-01-13T10:30:00",
  "replayed": 17
}

Testing a receiver

Send a synthetic inkfree.test event. It creates a real delivery record and exercises the full signing and delivery path, so it is the reliable way to confirm your signature verification works before going live.

curl
curl -X POST "https://api-uat.softsages.com/core/api/v1/inkfree/webhooks/whk_123/test" \
  -H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 response
{
  "delivery_id": "8f14e45f-ea6d-4b1f-9b3a-1c2d3e4f5a6b",
  "message": "Test webhook queued."
}

Full reference for all of these: Webhook operations.