Handle webhook deliveries

Acknowledge fast, handle repeats and out-of-order events, and fix failed deliveries

Build your endpoint to accept each delivery quickly, and to cope with repeats and events that arrive out of order. When something goes wrong, use the delivery log to see what happened and send the event again.


Before you start

  • An endpoint that verifies signatures. See Verify webhook signatures.
  • Somewhere to store the delivery IDs you have processed, such as a database table with a unique key.

Receive a delivery

  1. Verify the signature. If it fails, return 401 and stop.
  2. Read the X-BillPro-Delivery header. If you have already processed this delivery ID, return 200 and stop.
  3. Save the event, or put it on a queue.
  4. Return a 2xx status code straight away.
  5. Process the event in the background. Record the delivery ID when processing succeeds.

Keep step 4 fast. Don't call other systems or run slow work before you reply. A slow reply can count as a failed attempt, and BillPro sends the event again.


What counts as success

BillPro counts a delivery as Succeeded when your endpoint accepts it with a 2xx status code.

Anything else counts as a failed attempt. This includes other status codes, and attempts where your server can't be reached or doesn't answer. For those, Response is empty, and Delivery Details shows why the attempt couldn't be made.

If you return an error, include a short message in the response body. The delivery log shows the start of it in Response, which helps you debug.


Handle repeats

Delivery is at least once. You can receive the same event more than once, for example when your reply was lost or you clicked Send again.

The X-BillPro-Delivery header holds the delivery ID. It stays the same on every retry and every redelivery of the event. The delivery log shows the same value as Delivery ID.

To make your handler safe to repeat:

  • Store each delivery ID once processing succeeds, with a unique constraint.
  • Before you process an event, check whether its delivery ID is already stored. If it is, reply 200 and skip it.
  • Record the ID only after processing succeeds. If processing fails, you can then take the event again from a retry or a redelivery.

Handle ordering

BillPro doesn't guarantee the order of deliveries. An order can reach you as Active before you receive Pending, especially after a retry.

  • Use the occurred_at field in the payload to decide which event is newest for an order. It's the time the order reached the status. Don't apply an event older than the one you already have.
  • Some events have previous_status set to null, because BillPro couldn't tell what the status was before. Trust status and occurred_at for these.
  • If you need the current state, fetch the order with the API after you receive the event. The links.order URL in the payload points to it. See Show order.

Retries

If an attempt fails, BillPro retries the delivery automatically on a fixed schedule. The delivery shows Retrying, and Next attempt shows when BillPro will try again.

Each delivery gets up to 12 attempts: the first one, then 11 retries. The gaps between retries grow from 30 seconds to one week. Retrying lasts about 11 days in total.

When all 12 attempts have failed, the delivery shows Failed. BillPro stops trying. You can still send it again by hand.

If a delivery fails its whole retry schedule and nothing sent to the endpoint succeeds in that time, BillPro switches the endpoint off. Its status becomes Auto-disabled, the endpoint page shows the reason, and BillPro emails the merchant. BillPro sends nothing to it until you turn it back on.

Consecutive failures on the endpoint page is for information only. It doesn't decide when an endpoint is switched off.

To turn an auto-disabled endpoint back on:

  1. Fix the problem on your server.
  2. Open the endpoint, click Edit, set the status to Active, and click Save.
  3. Send the Failed and Canceled deliveries you still need again. BillPro doesn't resume them by itself. See Send a delivery again.

Delivery statuses

StatusMeaning
PendingQueued for its first attempt, or for a redelivery.
RetryingAt least one attempt failed. BillPro will try again at Next attempt.
SucceededYour endpoint accepted it with a 2xx code.
FailedAll 12 attempts failed. BillPro has stopped trying.
CanceledThe endpoint was switched off before the delivery was sent.

Use the delivery log

Each endpoint page has a Recent deliveries table, newest first, 20 to a page.

  1. From the user icon menu, select Webhooks.
  2. Click the endpoint URL.
  3. Scroll to Recent deliveries.
ColumnShows
UIDThe order ID. Click it to open the order. A dash for a test event, or if the order was deleted.
EventThe order event, such as Active.
Delivery IDThe value of the X-BillPro-Delivery header.
CreatedWhen the delivery was created.
StatusThe delivery status.
AttemptsHow many times BillPro has tried to send it.
ResponseThe last status code your endpoint returned.
Next attemptWhen BillPro will try again, if it is retrying.

Click a Delivery ID to open Delivery Details. It shows the timestamps, the last response code, the start of your endpoint's response, and the Signed payload. The signed payload is the exact JSON document that BillPro sent.

The endpoint page also shows Last success, Last failure and Consecutive failures for the endpoint. Consecutive failures counts failed attempts in a row, not deliveries, and goes back to zero after the next success. The Webhooks list shows Last Success and Last Failure for each endpoint.

The log is for checking recent problems, not a permanent archive. BillPro removes finished deliveries (Succeeded, Failed and Canceled) on a schedule. Keep your own records, keyed on the delivery ID, if you need a permanent history.


Send a delivery again

  1. Open the endpoint and find the delivery in Recent deliveries.
  2. Click Send again on the row.

BillPro sends the same document again, with the same delivery ID. Only the signature changes, because BillPro signs it again with a new timestamp. If you already processed that ID, your duplicate check will skip it.

  • For a Succeeded, Failed or Canceled delivery, BillPro resets it to Pending. The attempt count goes back to zero, and it gets a new retry schedule.
  • For a Retrying delivery, BillPro tries again straight away. It keeps its attempt count, so clicking Send again doesn't restart the 11-day schedule.

The previous Response is replaced by the new attempt's result.

Send again isn't available when:

  • the delivery is Pending, because it's already queued
  • the endpoint is Inactive or Auto-disabled. Turn it on first.

Troubleshooting

What you seeLikely causeWhat to do
Response is 401 on every deliveryThe signature check failsUse the raw body, not parsed JSON. Use the full secret with the whsec_ prefix. Check that you're using the secret for this endpoint.
401 on every delivery after a rotationYour server still has the old secretUpdate your server with the new secret. Then send the failed deliveries again. See Rotate a webhook signing secret.
Deliveries stay Retrying with no ResponseBillPro can't reach your serverOpen Delivery Details to see why the attempt couldn't be made. Check that the URL is public, uses https:// with a valid certificate, and that your firewall allows the requests.
Response is 404 or 405The path is wrong or doesn't accept POSTCheck the Endpoint URL and your route.
Response is 5xxYour code threw an errorCheck your server logs. Reply before you do slow work.
The endpoint shows Auto-disabledDeliveries kept failingFix the cause, set the status to Active, and send the failed deliveries again.
Send test event is greyed outThe endpoint is switched offSet the status to Active.
"Endpoint is disabled" when you send a test or a delivery againThe endpoint is switched offSet the status to Active.
Can't send more test eventsYou sent 10 to this endpoint in the last hourWait, or use Send again on an earlier test.
You processed the same event twiceRepeats aren't handledSkip delivery IDs you've already processed.
The address is refused when you saveIt's http://, private, internal or has credentialsUse a public https:// URL without a username or password.
Webhooks isn't in the menuYour user isn't an Admin, or doesn't have API AccessAsk an Admin to change your role. Contact BillPro to turn on API Access.

Recommended reading

Learn what each event contains

Did this page help you?