Skip to main content

Delivery log & replay

No support ticket needed to answer "did you send it?" — your webhook delivery history is queryable, and any delivered or failed event can be re-sent, via the API itself. This is also how you monitor your integration: the platform does not send alerts to your technical contact for exhausted deliveries or a circuit-breaker trip, so poll this endpoint (or the poll API) from your side.

Inspect deliveries​

GET /api/v1/partner/webhooks/deliveries?limit=50
GET /api/v1/partner/webhooks/deliveries?status=exhausted&since=2026-08-10T00:00:00Z
ParamDefault / limitMeaning
limitdefault 50, max 200Rows returned.
status—Filter: pending | in_flight | delivered | exhausted.
since—ISO 8601 datetime; only deliveries created (queued) at or after it — not delivered or exhausted after it.

Rows come back newest first (by creation time).

200
{
"success": true,
"deliveries": [
{
"eventId": "evt_66a1f0c2d3e4f5a6b7c8d9e0",
"sourceLoanNumber": "LN-2026-0001",
"sequence": 4,
"type": "payment.collected",
"status": "delivered",
"attempts": 1,
"nextAttemptAt": "2026-08-17T09:31:01.907Z",
"deliveredAt": "2026-08-17T09:31:02.418Z",
"lastStatusCode": 200,
"lastError": null,
"replayedAt": null,
"createdAt": "2026-08-17T09:31:01.907Z"
}
]
}
FieldMeaning
eventIdThe event's id — the same value as the X-Fieldproof-Delivery header and your dedupe key.
sourceLoanNumber, sequence, typeFrom the event envelope.
statuspending (queued for a first or next attempt), in_flight (a POST is running now), delivered (you answered 2xx), exhausted (all attempts failed — or, with lastError: "callback not configured/active" and typically attempts: 0, there was no active callback or no active key for this environment when it came due, so it was parked unsent; replay it once that is fixed).
attemptsPOSTs made so far.
nextAttemptAtWhen the next attempt is due (pending rows). It keeps its last value once a row is delivered or exhausted — ignore it then.
deliveredAtWhen you answered 2xx.
lastStatusCode, lastErrorWhat your endpoint returned last time — HTTP status, or the transport error (timeout, TLS, DNS…).
replayedAtSet if you replayed it (below).
createdAtWhen the delivery was queued.

exhausted = the full retry ladder failed (0s → 30s → 2m → 10m → 1h → 6h → 24h, 7 attempts, ≈31 hours). Delivery rows are kept for 30 days after they are delivered or exhausted; the event itself stays replayable for ≈13 months (below).

:::tip Monitor it A five-minute cron that calls GET /webhooks/deliveries?status=exhausted&since=<now − 48 h>&limit=200, remembers the eventIds it has already alerted on, and pages your on-call for new ones is the alerting you should have. Do not pass your last check time as since: it filters on when the delivery was created, and exhaustion comes ≈31 hours later, so that query never matches. Widen the window after a circuit-breaker trip or a replay — both keep the original creation time and push exhaustion later. lastError on the newest rows tells you why (401 → your signature check, 5xx → your handler, timeouts → respond faster). Quote the X-Request-Id response header when you write to [email protected]. :::

Replay​

POST /api/v1/partner/webhooks/deliveries/{eventId}/replay
200
{ "success": true, "status": "pending", "message": "<human-readable text>" }
  • Queues an immediate re-delivery of that event and resets its retry ladder — an exhausted row gets a fresh 7 attempts.
  • Works on delivered rows too (lost it after processing? replay it). Your receiver's eventId dedupe makes replays harmless by design.
  • If the row is already pending or in_flight, the response says so ("already queued") — nothing extra is scheduled.
  • Events with no delivery row — emitted while you had no callback configured, before you subscribed to that type, or whose row has aged out after 30 days — can be replayed too: the replay creates the delivery on demand from the event store (events are kept ≈13 months) and queues it. This needs an active callback now — otherwise 422 CALLBACK_NOT_CONFIGURED.
  • 404 DELIVERY_NOT_FOUND — no event with that eventId exists for your organisation (wrong id, or another organisation's event).

A replay is an ordinary delivery — same event body and eventId, freshly signed with a current timestamp — so your receiver needs nothing special. The replay call itself counts toward your 60 requests/min API rate limit.

Test your endpoint​

POST /api/v1/partner/webhooks/test

Fires a signed ping at your callback synchronously and returns the result:

200
{
"success": true,
"delivered": true,
"statusCode": 200,
"latencyMs": 212,
"error": null,
"bodyExcerpt": "{\"ok\":true}"
}
  • delivered: false with statusCode / error filled in tells you exactly what your endpoint did (a 401 here almost always means the signature was verified against a re-serialised body or the wrong path — see the receiver contract).
  • The ping envelope has sourceLoanNumber: null and sequence: null — your receiver must tolerate that (full body).
  • 422 CALLBACK_NOT_CONFIGURED — no active callback is registered for this environment (sandbox and production are configured separately).

Run it on every deploy of your receiver.