Webhooks
Webhook events are notifications triggered by specific operations in Bahn, allowing your backend to react when those operations occur — such as a payment succeeding, a payment being canceled, or a refund being processed.
Delivery
Bahn delivers webhook events as a JWS JSON Serialization request to your registered HTTPS endpoint.
POST <your-webhook-url>
Content-Type: application/jose+jsonThe request body is a signed JWS object in JSON Serialization format:
{
"payload": "<base64url-encoded event JSON>",
"signatures": [
{
"protected": "<base64url-encoded header>",
"signature": "<base64url-encoded signature>"
}
]
}To read the event, base64url-decode the payload field. The decoded value is the notification JSON. For example:
{
"payload": "eyJtZXJjaGFudF9pZCI6ImNfdGlja2V0IiwiZXZlbnRfaWQiOiI3NDI1NDhlNy00YzUzLTQwMjAtYmMyOC01NjU2N2ExYWY2ZDUiLCJldmVudF9uYW1lIjoicGF5bWVudF9pbnRlbnQuc3RhdHVzLnN1Y2NlZWRlZCIsImRhdGFfdHlwZSI6InBheW1lbnRfaW50ZW50IiwiZGF0YSI6eyJpZCI6ImM2YWY4ODU1LWNiZjYtNDJhMC05YzQ0LTM2ZGUwZGUwYTc5MyIsInN0YXR1cyI6InN1Y2NlZWRlZCIsImFtb3VudCI6IjE0MDAwIiwiY3VycmVuY3kiOiI3MDQiLCJwYXltZW50X21ldGhvZF9pZCI6IjAxOWNmNjY4LThiODktN2UyMi1hNmUzLWI4ODllY2M1NGI4NyIsImxhdGVzdF9jaGFyZ2VfaWQiOiI0ZjViMWFkNC0xYmI3LTQ0NTItOTMwMS04YWExMTI0MTc0YjAiLCJtZXRhZGF0YSI6eyJrZXlfMCI6InN0cmluZyIsImtleV8xIjoic3RyaW5nIn0sImNyZWF0ZWRfYXQiOiIyMDI2LTAzLTE2VDExOjI5OjI4Ljc5MzUxODYwMloiLCJ1cGRhdGVkX2F0IjoiMjAyNi0wMy0xNlQxMToyOToyOC44MjAwNjNaIiwiY2FuY2VsZWRfYXQiOm51bGx9LCJwdWJsaXNoZWRfYXQiOiIyMDI2LTAzLTE2VDE4OjI5OjM2LjI4MzIxNzA2NyswNzowMCJ9",
"signatures": [
{
"protected": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTc3MzY2MDU3Niwia2lkIjoiU2tQNDFYVXc3aGlUa0FmNlptYmU0byIsInR5cCI6IkpXUyIsInVybCI6Imh0dHBzOi8vYm4uY2FrZWRpZ2liYW5rLnRlY2gvbWVyY2hhbnQtc2ltdWxhdGlvbi93ZWJob29rIn0",
"signature": "HT-RvOtf0Y0cUoCCEBRMkBAa1Q00d2ZxWOsOo0E0mH47MCvOsBGe87aZr4fV-EhEsnOtZWRg-kavcgzGLrtNSw"
}
]
}{
"merchant_id": "c_ticket",
"event_id": "742548e7-4c53-4020-bc28-56567a1af6d5",
"event_name": "payment_intent.status.succeeded",
"data_type": "payment_intent",
"data": {
"id": "c6af8855-cbf6-42a0-9c44-36de0de0a793",
"status": "succeeded",
"amount": "14000",
"currency": "704",
"payment_method_id": "019cf668-8b89-7e22-a6e3-b889ecc54b87",
"latest_charge_id": "4f5b1ad4-1bb7-4452-9301-8aa1124174b0",
"metadata": {
"key_0": "string",
"key_1": "string"
},
"created_at": "2026-03-16T11:29:28.793518602Z",
"updated_at": "2026-03-16T11:29:28.820063Z",
"canceled_at": null
},
"published_at": "2026-03-16T18:29:36.283217067+07:00"
}Notification Fields
| Field | Type | Description |
|---|---|---|
merchant_id | string | ID of the merchant this event belongs to. |
event_id | string | Unique identifier for this event delivery (UUID). Use this to deduplicate deliveries. |
event_name | string | Name of the event (e.g. payment_intent.status.succeeded). See Events for the full list. |
data_type | string | Type of the object in data. One of: payment_intent, charge, refund, authorization, transaction. |
data | object | A snapshot of the resource at the time the event was triggered. Shape depends on data_type. See Events for the full field reference. |
published_at | string | ISO 8601 timestamp of when the event was published. |
Getting Started
Register Your Endpoint
To receive webhook events, expose an HTTPS endpoint in your backend that accepts POST requests and returns a 200 status code on success. Your webhook endpoint is registered during merchant onboarding — provide Bahn with your HTTPS endpoint URL at that time.
INFO
Your endpoint must respond with HTTP 200 to acknowledge receipt. Any other status code is treated as a delivery failure and will trigger a retry.
Verify the Signature
Every request is signed by Bahn. Before processing any event, verify the JWS signature to confirm the request was genuinely sent by Bahn and has not been tampered with.
See Webhook Signatures for the full verification steps. Reject any request that fails signature verification.
Handle Duplicates
Delivery is not strictly once — in rare cases an event may be delivered more than once. Always check the event_id field and safely ignore events you have already processed.
Retries
WIP