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 shows a dash, and Delivery Details shows why the attempt couldn't be made.

If you return an error, include a short message in the response body. Delivery Details shows the start of it under 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, such as order.draft for a new order. 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. Retrying lasts about 12 days in total.

RetryWait after the failed attempt
130 seconds
22 minutes
310 minutes
430 minutes
51 hour
6 to 9About 2 days in total
103 days
117 days

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 profile menu, select Webhooks.
  2. Click the endpoint URL.
  3. Scroll to Recent deliveries.
Recent deliveries with Succeeded, Retrying and Failed rows

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.

Times are in UTC.

Click a Delivery ID to open Delivery Details. It shows when the event occurred, the last and next attempt, the Response code, the start of your endpoint's reply under Response, and the Signed payload. The signed payload is the document BillPro sent, laid out for reading. You can't use it to check a signature.

The Delivery Details pop-up with the signed payload

Delivery details

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 12-day schedule.

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

Send again doesn't appear 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 failingThe reason shows under the status on the endpoint page. Fix 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.
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 profile menuYour user doesn't have the admin_user role, 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?