What counts as delivered
Your server must answer with a success status, any status from200 to 299 (a “2xx” status), within 15 seconds. Fifteen seconds is ample for storing an event and answering; it is not meant for doing the work. Anything else is a failure and is retried:
- any other status, including
3xx: redirects are never followed, so register the final URL; - no answer within 15 seconds, or no connection within 5;
- a TLS error, such as an expired or self-signed certificate.
Retry schedule
This is the example schedule from the Standard Webhooks specification. Each wait varies by up to 20% either way. When a busy receiver fails many deliveries at once, this spreads the retries out so they do not all return at the same moment. The whole schedule covers about 75.6 hours (just over three days), long enough to ride out a weekend outage. If attempt 10 fails, the delivery is
Failed and not retried again. You can still resend it at any time.
Every attempt of the same delivery carries the same webhook-id and the same body. X-Flow-Delivery-Attempt says which attempt it is.
Answers that change what happens next
Ordering
By default deliveries are not ordered: events are sent as they happen, and a retry can arrive after a later event. Usetimestamp to tell which came first, or fetch the latest state from resourceUrl.
If you need strict order, create the endpoint with "ordering": "Fifo". Its deliveries then arrive one at a time, in the order the events happened. The cost is that a delivery your server keeps rejecting holds up every event behind it until it succeeds or runs out of retries, so only choose it if order matters more than speed.
Rate limits
SetrateLimitPerSecond (1 to 1,000) on an endpoint to cap how fast Flow Payroll sends to it. Deliveries over the limit wait, not fail. Remove the cap with PATCH /endpoints/{endpointId} and {"rateLimitPerSecond": null}.
When an endpoint keeps failing
- Ten failures in a row pause deliveries to the endpoint for a minute, then for longer each time it happens again, up to 30 minutes. One delivery is tried after each pause, and when it succeeds everything resumes. Waiting deliveries do not use up their attempts.
- A day of continuous failure sends an email, so someone can fix your receiver before it is disabled. It goes to the endpoint’s alert contacts, or else your organisation’s or account’s default alert contacts, or else the person who created the endpoint and the account owner. See Alert emails. You get one email for each run of failures.
- Five days of continuous failure disable the endpoint. Its status becomes
DisabledBySystemwithstatusReasonfailing_for_5_days, new events are recorded asSkipped, and the same people are emailed again.
PATCH /endpoints/{endpointId} with {"status": "Active"}. It starts with a clean record. Then replay what it missed.
Operational events
Emails suit one organisation. If you run payroll for hundreds, you want to act on a failing endpoint in code: open a ticket, page whoever is on call, or re-enable it once it is fixed. Subscribe another endpoint to these events and Flow Payroll sends them to it:- Who receives them. For an organisation’s endpoint: that organisation’s other endpoints and your account-wide endpoints. For an account-wide endpoint: your other account-wide endpoints. Never the endpoint the event is about, because it is the one that cannot receive it.
- What they carry. The endpoint’s id, URL and description,
organisationId,customerId,failingSince, the last status code and error token, andmetadata.resourceUrlpointing at the endpoint (or, for an exhausted delivery, the delivery). Fields that do not apply arenull. - They never cascade. If the endpoint receiving these events fails too, its failures are emailed as usual but raise no further operational events.
webhook.* for all three. They are recorded, listed and replayable like any other event.
Pausing an endpoint yourself
PATCH /endpoints/{endpointId} with {"status": "Paused"} stops deliveries while you work on your receiver. Events that happen meanwhile are recorded as Skipped, not queued. Set the status back to Active and replay the window with "statuses": ["Skipped"] to receive them.