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
https://bankhub-api-sandbox.sepay.vn/v1/webhookRequest Body
Webhook endpoint URL (HTTPS required in production)
Authentication type
Secret key (will be sent in X-Secret-Key header)
1 = enabled, 0 = disabled
Default:1["*"] = all events, [] = no events
- 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 RequestInvalid URL, internal IP blocked (SSRF protection), or invalid event name
401UnauthorizedAccess token is invalid or expired
API Response
{
"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": [
"*"
]
}
}HTTP status code
Error code (if any)
Result messages
Webhook configuration (full secret key - only in POST response)
Code Examples
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
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": ["*"]}'
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": []}'
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"]}'
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[]ornull: Don't send any 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_CHANGEDLINK_SESSION_COMPLETED,LINK_SESSION_FAILED,LINK_SESSION_ABANDONED,LINK_SESSION_EXPIRED
Bank Account Events (3):
BANK_ACCOUNT_LINKED,BANK_ACCOUNT_UNLINKED,BANK_ACCOUNT_INACTIVATED
- 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
- 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 ifallow_eventsis configured - Immediate Effect: Configuration changes apply immediately to all new events
When auth_type = "SECRET_KEY", each webhook request will have the header:
X-Secret-Key: your-secret-key-here
Verify webhook in your endpoint:
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 eventconst event = req.body;console.log('Event:', event.event);res.status(200).send('OK');});