Skip to main content
Your receiver will be down at some point. Nothing is lost when it is: every event and every attempt is recorded, and you can send anything again.

The delivery log

  • GET /deliveries?endpointId={endpointId} lists an endpoint’s deliveries, newest first. Add status=Failed&status=Pending to see only what has not arrived. An unknown status is a 400 with invalid_delivery_status, not ignored.
  • GET /deliveries?eventId={eventId} lists every endpoint one event went to.
  • GET /deliveries/{deliveryId} shows one delivery and every attempt at it: when it was sent, the headers we sent, how long your server took, the status it answered and the start of its response body.
Deliveries are kept for a year and their attempts for 90 days. Events are kept for 90 days, and GET /events lists them, each with the body exactly as it was sent. Filter it by type with eventType, repeated for each type or family (eventType=payslip.*&eventType=payroll_run.completed), up to 10 per request; more is a 400 with too_many_event_types. Every list is paged. Each page is full except the last; follow paginationToken until it is absent.

Catch up from the event list

After your receiver was down, you can read what you missed from GET /events instead of replaying it. Ask for up to 100 events a page with pageSize, and pass the content.metadata.paginationToken from each answer back as paginationToken for the next page. Every page is full except the last, whose paginationToken is null. Events come newest first. Each one’s envelope is the body exactly as it was delivered and its eventId is the webhook-id, so the same duplicate check covers both. The token is short-lived: use it straight away and do not store it.

What Flow Payroll keeps

When its time is up, a record is deleted automatically, usually within a few days. These records are encrypted at rest. Signing secrets, signing keys and custom header values are also encrypted with an encryption key used only by the Webhooks service.

Resend one delivery

POST /deliveries/{deliveryId}/replays sends a finished delivery again, straight away, as a new delivery that points back at the original. It carries the same event id in webhook-id, so a receiver that handles duplicates is safe. Send an Idempotency-Key header to make sure a retried request resends only once. A delivery that is still being retried cannot be resent until it finishes.

Replay a window

After an outage, replay everything that failed:
The answer is 202 Accepted with the replay’s id. It runs in the background: follow it with GET /replays/{replayId}, which shows how many deliveries it has resent so far and when it finished. A replay never sends the same thing twice, even if it is interrupted and runs again. You can run three at a time. Cancel one that is still running with PATCH /replays/{replayId} and {"status": "Cancelled"}.

After your endpoint was disabled

  1. Fix the cause. The endpoint’s statusReason and its last deliveries’ lastErrorToken say what went wrong.
  2. PATCH /endpoints/{endpointId} with {"status": "Active"}.
  3. Replay the window with "statuses": ["Failed", "Skipped"]: Failed for what was tried before it was disabled, Skipped for what happened after.