Webhooks
Audience: 🔌 Integration Client
BankConnector POSTs signed events to your endpoint as payments move through their lifecycle. This page covers managing and configuring endpoints. For the event envelope, signature verification, retries and the event catalog, see Receiving results. To post into Teams or Slack, see Teams and Slack webhook URLs.
Register an endpoint
curl -X POST https://sandbox.bankconnector.com/api/webhooks \
-H "X-API-Key: $BC_KEY" -H "Content-Type: application/json" \
-d '{
"companyId": "comp_…",
"url": "https://erp.example.com/hooks/bankconnector",
"eventTypes": ["payment.sent", "payment.executed", "payment.rejected"]
}'
The 201 body has two top-level keys, endpoint and secret. The endpoint id is at endpoint.id (there is no top-level id):
{
"endpoint": {
"id": "wh_…", // ← the id you pass to every /api/webhooks/<id>/… route
"url": "https://erp.example.com/hooks/bankconnector",
"eventTypes": ["payment.sent", "payment.executed", "payment.rejected"],
"enabled": true,
// …
},
"secret": "whsec_…", // ← the signing secret, shown ONCE
}
Read the id as body.endpoint.id. body.id is undefined.
| Field | Required | Notes |
|---|---|---|
companyId | required | scope: the API key's own company |
url | required | for every transport, including slack and email; omitting it is a 400 url_required. Max 2048 chars. For the https transport it must be HTTPS (SSRF-guarded, IP-pinned) |
eventTypes | optional | array of event names. Omit (or send ["*"]) to receive every event in the catalog; an empty [] is rejected with a 400 |
transport | optional | "https" (default), "slack", "teams", or "email" |
emailTo | optional (email) | destination address for the email transport. Omit it and the event is emailed to the company's admins instead |
filterPredicate | optional | { "bankKey": "danske-dk", "minAmount": 1000 }: deliver only matching events. minAmount/maxAmount are in EUR: an event in another currency is compared on its EUR equivalent at the cached reference rate, and one in a currency with no rate is delivered whatever its amount |
Capture
secreton creation: it's thewhsec_…HMAC signing key and is shown only once. You verify every delivery against it (see Receiving results).
Unknown fields are rejected with 400 validation_failed.
If webhooks are not enabled for your organisation, registration answers 403 capability_disabled and no events are sent.
Managing endpoints
| Method & path | What it does |
|---|---|
GET /api/webhooks | list your endpoints (metadata only, never the secret) |
GET /api/webhooks/<id> | fetch one endpoint |
DELETE /api/webhooks/<id> | remove an endpoint |
POST /api/webhooks/<id>/enable, /disable | pause / resume deliveries |
POST /api/webhooks/<id>/test | send a webhook.test event through the real signing + delivery path |
GET /api/webhooks/<id>/deliveries | keyset-paginated delivery attempts (newest first; see Pagination) |
POST /api/webhooks/<id>/deliveries/<deliveryId>/redeliver | replay one delivery as a new attempt |
POST /api/webhooks/<id>/redeliver-failed?since=<iso> | bulk-replay every failed delivery since a timestamp |
POST /api/webhooks/<id>/re-enable | clear an auto-disabled endpoint (see below) |
POST /api/webhooks/<id>/rotate-secret | rotate the signing secret; body { "overlapHours": 24 } (default 24, clamped 1–72) |
Managing webhooks needs an admin session or an API key (for the key's own company).
Reliability
- Delivery is at-least-once, so deduplicate on the event
id. Success is any HTTP2xx; each attempt times out at 10 s. - 31 delivery attempts, exponential backoff 30 s → 1 m → 2 m → 4 m → 8 m → 16 m → 32 m → 1 h between them, then a flat 1 h wait for each of the 23 attempts after that, about 1443.5 minutes (roughly 24 hours) end to end (±20% jitter). After the 31st attempt the delivery settles
failed; recover it withPOST /api/webhooks/<id>/redeliver-failed?since=<iso>or the single-deliveryredeliverabove. - Circuit breaker: after 5 consecutive failed events an endpoint is auto-disabled and stops receiving until you
POST /api/webhooks/<id>/re-enable. Thewebhook.auto-disabledtransition is audit-log only (not itself a webhook), so back webhooks with a periodic poll and watch theX-Delivery-Sequenceheader for gaps.
Secret rotation
POST /api/webhooks/<id>/rotate-secret issues a new secret while keeping the old one valid for overlapHours. During the overlap, deliveries carry multiple v1= signatures (old and new): accept a match against any of them, then cut over to the new secret. See Receiving results.
Transports
- HTTPS (default): a signed
POSTto yoururl. - Slack: set
transport: "slack"and makeurlyour Slack incoming-webhook URL. - Microsoft Teams: set
transport: "teams"and makeurlyour Teams Workflow webhook URL. Each event is posted as a card. Teams and Slack webhook URLs shows how to get either URL. - Email: set
transport: "email"and (optionally)emailTo; events are formatted as email and go to the company's admins whenemailTois omitted.urlis still required, although the email transport doesn't POST to it.