Skip to content

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+json

The request body is a signed JWS object in JSON Serialization format:

json
{
  "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:

json
{
  "payload": "eyJtZXJjaGFudF9pZCI6ImNfdGlja2V0IiwiZXZlbnRfaWQiOiI3NDI1NDhlNy00YzUzLTQwMjAtYmMyOC01NjU2N2ExYWY2ZDUiLCJldmVudF9uYW1lIjoicGF5bWVudF9pbnRlbnQuc3RhdHVzLnN1Y2NlZWRlZCIsImRhdGFfdHlwZSI6InBheW1lbnRfaW50ZW50IiwiZGF0YSI6eyJpZCI6ImM2YWY4ODU1LWNiZjYtNDJhMC05YzQ0LTM2ZGUwZGUwYTc5MyIsInN0YXR1cyI6InN1Y2NlZWRlZCIsImFtb3VudCI6IjE0MDAwIiwiY3VycmVuY3kiOiI3MDQiLCJwYXltZW50X21ldGhvZF9pZCI6IjAxOWNmNjY4LThiODktN2UyMi1hNmUzLWI4ODllY2M1NGI4NyIsImxhdGVzdF9jaGFyZ2VfaWQiOiI0ZjViMWFkNC0xYmI3LTQ0NTItOTMwMS04YWExMTI0MTc0YjAiLCJtZXRhZGF0YSI6eyJrZXlfMCI6InN0cmluZyIsImtleV8xIjoic3RyaW5nIn0sImNyZWF0ZWRfYXQiOiIyMDI2LTAzLTE2VDExOjI5OjI4Ljc5MzUxODYwMloiLCJ1cGRhdGVkX2F0IjoiMjAyNi0wMy0xNlQxMToyOToyOC44MjAwNjNaIiwiY2FuY2VsZWRfYXQiOm51bGx9LCJwdWJsaXNoZWRfYXQiOiIyMDI2LTAzLTE2VDE4OjI5OjM2LjI4MzIxNzA2NyswNzowMCJ9",
  "signatures": [
    {
      "protected": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTc3MzY2MDU3Niwia2lkIjoiU2tQNDFYVXc3aGlUa0FmNlptYmU0byIsInR5cCI6IkpXUyIsInVybCI6Imh0dHBzOi8vYm4uY2FrZWRpZ2liYW5rLnRlY2gvbWVyY2hhbnQtc2ltdWxhdGlvbi93ZWJob29rIn0",
      "signature": "HT-RvOtf0Y0cUoCCEBRMkBAa1Q00d2ZxWOsOo0E0mH47MCvOsBGe87aZr4fV-EhEsnOtZWRg-kavcgzGLrtNSw"
    }
  ]
}
json
{
  "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 ​

FieldTypeDescription
merchant_idstringID of the merchant this event belongs to.
event_idstringUnique identifier for this event delivery (UUID). Use this to deduplicate deliveries.
event_namestringName of the event (e.g. payment_intent.status.succeeded). See Events for the full list.
data_typestringType of the object in data. One of: payment_intent, charge, refund, authorization, transaction.
dataobjectA snapshot of the resource at the time the event was triggered. Shape depends on data_type. See Events for the full field reference.
published_atstringISO 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

Developers