Delivery and retries
When an event matches your subscription, BankCore writes one delivery row and a poller picks it up. The poller is the only thing that sends webhooks, so its cadence is the floor on latency: expect the first attempt within about ten seconds of the event, longer when the platform is paying out a large batch.
Timeouts
Section titled “Timeouts”| Phase | Limit |
|---|---|
| TCP connect | 5 seconds |
| Waiting for your response | 10 seconds |
A slow endpoint is a failed endpoint. Verify the signature, write the event somewhere durable, answer 2xx, and do the work afterwards.
What your response means
Section titled “What your response means”| You answer | We record | What happens next |
|---|---|---|
Any 2xx |
DELIVERED |
Nothing more. |
Any 4xx |
FAILED on that attempt |
Never retried. The same bytes would get the same answer. Does not count toward auto-disable. |
Any 5xx |
RETRYING |
Back on the schedule below. |
| No answer at all (timeout, connection refused, DNS failure) | RETRYING |
Back on the schedule below. |
The 4xx rule is deliberate: every event fans out to every subscriber, and a subscriber rejecting a type it does not want should not burn retries. Its consequence is that a signature or freshness check that answers 401 drops that event permanently. Answer 5xx when the failure might be on your side.
Retry schedule
Section titled “Retry schedule”Each delivery is attempted up to 10 times. After the first failure the next attempt is 5 minutes later, then 30 minutes, 2 hours, 6 hours, 12 hours, and then 24 hours between each remaining attempt. The whole ladder spans a little under five days. Each attempt is signed afresh; X-Webhook-Id does not change.
When the tenth attempt fails the delivery is marked FAILED and the subscription’s consecutive-failure count goes up by one. A single success resets that count to zero.
Delivery statuses
Section titled “Delivery statuses”| Status | Meaning |
|---|---|
PENDING |
Queued, never attempted. Served before the retry backlog. |
RETRYING |
Attempted, failed with a 5xx or no answer, waiting for its next slot. |
DELIVERED |
You answered 2xx. Terminal. |
FAILED |
You answered 4xx, or ten attempts were used up. Terminal unless retried. |
CANCELLED |
Never sent on purpose: the subscription was disabled, or the event type was removed from it, after the event had been queued. Not retryable from the portal. |
DELIVERING |
Reserved. The sender claims rows with a database lock rather than this status, so you will not normally see it. |
Delivery rows in a terminal state are kept for 90 days and then purged.
Ordering and duplicates
Section titled “Ordering and duplicates”Ordering is not guaranteed. Retries interleave with fresh events, and one subscription’s work is spread across a small number of parallel lanes, so two events for the same account can arrive out of order or at the same moment. Order by the payload’s own fields (reference, timestamp), not by arrival.
Duplicates do happen: a 2xx that was sent but lost on the wire is a retry on our side, and a replay or resend is a second delivery of the same event by design. Consume idempotently:
-
Treat
X-Webhook-Idas the key for retries of the same delivery. It is identical on every attempt. -
Treat the body’s
eventId(and for transactions,reference) as the key across replays. A replayed or resent event gets a newX-Webhook-Idof the formreplay-<uuid>orresend-<uuid>, but the body is byte-for-byte the original, soeventIdandreferenceare unchanged. -
Claim the key in your store before you act, inside a unique constraint, and answer
2xxon a duplicate without doing the work again.
Getting a missed event again
Section titled “Getting a missed event again”Three paths, from narrowest to widest.
Retry one delivery yourself. A delivery in FAILED can be re-queued from the portal. It resets the attempt counter and goes out on the next poll with the same X-Webhook-Id. Only Administrator and Operations roles can do it, and each subscription is limited to 20 retries per hour; a bigger backlog is a replay, below.
This is a portal-session call (the button on a delivery’s page), not a partner-API call, and takes no body. It answers 202 Accepted; the message has been queued, not yet sent. Refusals use the portal’s error shape:
errorCode |
When |
|---|---|
PORTAL_RETRY_NOT_PERMITTED |
Your portal role can read deliveries but not re-send. |
PORTAL_WEBHOOK_NOT_RETRYABLE |
The delivery is not FAILED. Pending and retrying rows are already on their way; delivered ones have arrived; cancelled ones need the bank. |
PORTAL_WEBHOOK_RETRY_LIMIT |
More than 20 retries on this subscription in the last hour. |
PORTAL_WEBHOOK_DELIVERY_NOT_FOUND |
Not a delivery on one of your subscriptions. |
Replay a window. The bank can replay every event delivered to your subscription between two instants, optionally narrowed to certain event types. Each replayed event is a fresh delivery (replay-<uuid>) carrying the original payload, flagged as a replay in the portal. Replay works only on an enabled subscription and is how you recover events that were dropped with a 4xx, or lost while your secret was being rotated.
Resend one event. The bank can also resend a single delivery regardless of its state. At the time of writing that tool targets the bank’s own channel subscriptions, not partner subscriptions; for a partner, ask for a replay of the window instead.
In the portal
Section titled “In the portal”Webhook endpoints lists your subscriptions with their status and disabled reason, if any. Opening one shows Deliveries for a date window, filterable by status and event type, with the status, HTTP answer and attempt count per row. A delivery’s page shows What happened, The message that was sent, What your server said, and for a failed one a Re-send button.