Create or Update Webhook API

API to create a new webhook or update an existing webhook (upsert). Webhooks are used to receive event notifications from Bank Hub.


API Endpoint

POST
https://bankhub-api-sandbox.sepay.vn/v1/webhook

Request Body

webhook_urlstring

Webhook endpoint URL (HTTPS required in production)

auth_typeenum

Authentication type

secret_keystring

Secret key (will be sent in X-Secret-Key header)

activeenum

1 = enabled, 0 = disabled

Default: 1
allow_eventsarray<object>

["*"] = all events, [] = no events

Note
  • This API requires a Bearer Token in the Authorization header
  • Upsert pattern: creates if doesn't exist, updates if exists
  • Only one webhook per merchant is allowed
  • Full secret key is returned in response for reference

Error Handling

400Bad Request

Invalid URL, internal IP blocked (SSRF protection), or invalid event name

401Unauthorized

Access token is invalid or expired

API Response

Response 200 - Upsert Successful
{
  "status": 200,
  "error": "string",
  "messages": {
    "success": [
      "string"
    ],
    "error": [
      "string"
    ]
  },
  "data": {
    "webhook_url": "https://example.com",
    "auth_type": "SECRET_KEY",
    "secret_key": "string",
    "request_content_type": "string",
    "active": 0,
    "allow_events": [
      "*"
    ]
  }
}
statusinteger

HTTP status code

errorstring

Error code (if any)

messagesobject

Result messages

dataobject

Webhook configuration (full secret key - only in POST response)

Code Examples

1
2
3
4
5
curl --request POST \
--url https://bankhub-api-sandbox.sepay.vn/v1/webhook \
--header 'Authorization: Bearer REPLACE_BEARER_TOKEN' \
--header 'content-type: application/json' \
--data '{"webhook_url":"http://example.com","auth_type":"SECRET_KEY","secret_key":"string","active":0,"allow_events":["*"]}'

Usage Examples

JSSubscribe to all events
1
2
3
4
5
6
7
8
9
10
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"auth_type": "SECRET_KEY",
"secret_key": "your-secret-key-here",
"active": 1,
"allow_events": ["*"]
}'
JSDisable all events
1
2
3
4
5
6
7
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"allow_events": []
}'
JSSubscribe to specific events
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl --location 'https://bankhub-api-sandbox.sepay.vn/v1/webhook' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{
"webhook_url": "https://merchant.com/webhook",
"auth_type": "SECRET_KEY",
"secret_key": "your-secret-key-here",
"active": 1,
"allow_events": [
"BANK_ACCOUNT_LINKED",
"BANK_ACCOUNT_UNLINKED",
"LINK_TOKEN_CREATED",
"LINK_SESSION_COMPLETED"
]
}'

Validation Rules

Webhook URL:

  • Must be HTTPS in production
  • Maximum 2000 characters
  • Cannot use internal IPs (SSRF protection)
  • Blocked IP ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16

Secret Key:

  • Maximum 500 characters
  • Recommended: random string, 32-64 characters length

Allow Events:

  • Only accepts valid event names from whitelist (13 events)
  • ["*"]: Wildcard to subscribe to all events
  • [] or null: Don't send any events
13 Webhook Events

Link Token Events (3):

  • LINK_TOKEN_CREATED, LINK_TOKEN_REVOKED, LINK_TOKEN_EXPIRED

Link Session Events (7):

  • LINK_SESSION_INITIALIZED, LINK_SESSION_REINITIALIZED, LINK_SESSION_STATE_CHANGED
  • LINK_SESSION_COMPLETED, LINK_SESSION_FAILED, LINK_SESSION_ABANDONED, LINK_SESSION_EXPIRED

Bank Account Events (3):

  • BANK_ACCOUNT_LINKED, BANK_ACCOUNT_UNLINKED, BANK_ACCOUNT_INACTIVATED
Upsert Behavior
  • Create new: If merchant doesn't have webhook, creates new webhook
  • Update: If merchant already has webhook, updates current configuration
  • One webhook per merchant: Only one webhook config allowed per merchant
  • Partial update: Only send fields that need updating
Important Note
  • SSRF Protection: Internal IPs and localhost are blocked to protect against SSRF attacks
  • Secret Key Display: Full secret key is only shown in POST response, GET response will mask it
  • Event Validation: Invalid event names will be rejected with error 400
  • Active Flag: If active = 0, webhook will not send any events even if allow_events is configured
  • Immediate Effect: Configuration changes apply immediately to all new events
Webhook Authentication

When auth_type = "SECRET_KEY", each webhook request will have the header: X-Secret-Key: your-secret-key-here

Verify webhook in your endpoint:

JSJavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.post('/webhook', (req, res) => {
const receivedSecret = req.headers['x-secret-key'];
const expectedSecret = 'your-secret-key-here';
 
if (receivedSecret !== expectedSecret) {
return res.status(401).send('Unauthorized');
}
 
// Process webhook event
const event = req.body;
console.log('Event:', event.event);
 
res.status(200).send('OK');
});