Prerequisites
- A publicly reachable HTTPS endpoint on your server ready to accept
POSTrequests. - Access to your Bachs developer portal.
Setup
You can manage webhook endpoints two ways: from the dashboard (below), or with the Webhook Endpoint API using an API key that has thewebhooks:write scope. Both create the same endpoints with the same signing secrets.
In the dashboard, your role needs permission to manage webhooks. You need it to add, edit, enable, disable or delete an endpoint, to see or rotate its signing secret, and to resend events. The Owner, Super Admin and Developer roles have it by default. The Finance and Customer Support roles do not. API keys use permissions instead:
webhooks:write for changes, and webhooks:read to read endpoints and signing secrets.1
Open the Developer Portal
From your dashboard, click on your Developer Portal at the bottom left

2
Go to Webhooks
In the developer portal, navigate to the Webhooks section.

3
Create a new endpoint
Click Add destination and enter the HTTPS URL where Bachs should deliver events. PS: All endpoints added automatically have a signing secret.
We use it to sign every delivery so you can verify requests are genuinely from Bachs.

All webhook deliveries include an
X-Bachs-Signature header. Validate it against the raw request body before processing the payload.4
Select events
Choose the events you want to receive. You can subscribe to payment events, withdrawal events, or both. Only the events you select will be delivered to your endpoint.
Once you save, your endpoint is live and will start receiving events immediately.
Verifying your webhooks
Every webhook delivery is signed. Before processing any payload, verify the signature to confirm the request came from Bachs.Retrieve your signing secret
Each endpoint has an auto-generated signing secret. To find it:1
Open the Developer Portal and go to Webhooks
Navigate to the Webhooks section in the developer portal.

2
Open your endpoint
Click on the endpoint you want to verify deliveries for.

3
Copy the signing secret
The signing secret is displayed on the endpoint detail page. Copy it and store it securely in your environment variables.

How the signature works
Each delivery includes these headers:
To verify, reconstruct the signed message using the timestamp and the raw request body, compute the HMAC-SHA256 using your secret, and compare it to the signature.
When verifying
X-Bachs-Signature-V2, split on ,, take the t= value as the timestamp, and accept the delivery if any v1= value matches what you computed. Comparing against only the first one will fail during a rotation.
Rotating a signing secret
Rotating adds a new secret and keeps the previous one valid for 24 hours. During that window every delivery is signed with both, so traffic keeps verifying while you deploy:- Rotate the secret and store the new value returned to you.
- Deploy it. Until you do, the old secret still matches.
- The old secret stops signing when the window closes.
Verification examples
Receiving Events
Every webhook delivery is aPOST request with a JSON body. The envelope looks like this:
id field to deduplicate deliveries. We guarantee at-least-once delivery, so the same event may arrive more than once.
Responding to deliveries
Return400 only for a delivery you reject, such as one with an invalid signature. If your own processing fails, for example a database write, return a 5xx so the delivery is retried. See Retries for which responses are retried, and when.
Events are not guaranteed to arrive in the order they happened, and a retried event can arrive after a newer one. Before you overwrite stored state, check that the event is not older than the state you already have, for example by comparing created_at.
Retries
A delivery succeeds when your endpoint returns any2xx status within 10 seconds. Anything else is a failed try. What we do next depends on how it failed:
Each delivery gets up to 5 tries: the first one, then retries about 1 minute, 5 minutes, 15 minutes and 1 hour after each failed try. The last try comes about 80 minutes after the first. After the fifth failure we stop. To send the event again, resend or replay it.
A resend or a replay is a single try. We do not retry it.
When an endpoint keeps failing
If every delivery to an endpoint fails for days, we warn you and then turn the endpoint off. This is what happens:- After 24 hours of failures, we email the account owner and admins. The email names the endpoint, says when it started failing, and gives the date we will turn it off.
- After 72 hours of failures, and at least 48 hours after that email, we turn the endpoint off and email the account owner and admins again. On the endpoint,
enabledbecomesfalse,disabled_atis set, anddisabled_reasonisconsecutive_failures.
401.
You always get the full 48 hours after the warning, even when the endpoint has already been failing for 72 hours. We only check the times when a delivery fails, so an endpoint that receives no events in that window stays on.
While an endpoint is off
- We send it no new events.
- Retries that were already waiting stop.
- Events that happen while it is off are not sent later, even after you turn it back on. They are still recorded on your account, so you can resend them.
Turn it back on
Fix the cause first: check that the URL is reachable and returns a2xx. If it still fails after you turn it on, the 24-hour count starts again from the next failure.
- Dashboard
- API
Open the endpoint from Webhooks in the developer portal. An endpoint we turned off shows a notice with the date we turned it off. Click Enable endpoint on the notice, or turn on the Enabled switch.
The endpoint list shows each endpoint’s status: Enabled, Disabled, or Disabled automatically when we turned it off.
The endpoint list shows each endpoint’s status: Enabled, Disabled, or Disabled automatically when we turned it off.
disabled_at and disabled_reason.
Then send the events it missed. List your events, keep the ones created after disabled_at whose type the endpoint subscribes to, and resend each one to the endpoint. Your handler should already ignore an event id it has seen, so a duplicate does no harm.
Turn an endpoint off yourself
Send{ "enabled": false } to Update a webhook endpoint, or turn off the Enabled switch on the endpoint page in the dashboard and confirm.

disabled_at and disabled_reason stay null, so you can tell your choice from ours.
Connect events
If you run a Connect platform, an endpoint can also receive events that happened on your connected accounts. Each endpoint carries anevent_source:
An event happens on one account, its origin. The origin’s own endpoints receive it on
account or all, and the origin’s parent receives it on connect or all. Delivery walks up one level and no further, so an event never reaches a sibling account.
On an event from a connected account, organization_id is that connected account rather than your platform, and a top-level account field carries the same id. Read the account from the payload rather than assuming the event belongs to the account you authenticated as.
Event reference
Every event has its own page with the payload shape and a field reference. Browse them under Events in the sidebar, grouped by resource:- Checkout:
checkout.completed,checkout.expired - Payments:
collection.succeeded,collection.failed,collection.underpaid - Payment methods:
payment_method.saved - Subscriptions:
customer.subscription.created,customer.subscription.updated,customer.subscription.deleted - Invoices:
invoice.created,invoice.paid,invoice.payment_failed - Withdrawals:
payout.created,payout.paid,payout.failed - Refunds:
refund.created,refund.paid,refund.failed - Disputes:
dispute.created,dispute.updated - Conversions:
conversion.completed,conversion.failed - Customers:
customer.created,customer.updated - Connect:
account.updated,capability.updated,transfer.created

