Webhooks
Receive real-time notifications when events occur in your Open Banking integration—consent changes, payment status updates, and account activity.
Overview
Webhooks allow your application to receive push notifications about events instead of polling the API. When an event occurs, we send an HTTP POST request to your configured endpoint with details about the event.
HTTPS Required
Webhook endpoints must use HTTPS in production. HTTP endpoints are only accepted in the sandbox environment.
Event Types
Subscribe to specific event types based on your integration needs:
Consent Events
Triggered when consent status changes. Essential for tracking authorization flow completion.
| Event Type | Description | When Triggered |
|---|---|---|
consent.authorized | Consent was authorized by the PSU | User completes consent approval |
consent.rejected | Consent was rejected by the PSU | User denies consent request |
consent.revoked | Consent was revoked | User or TPP revokes active consent |
consent.expired | Consent has expired | Consent passes its expiration date |
Payment Events
Triggered during payment lifecycle. Use these to update your UI and notify users.
| Event Type | Description | When Triggered |
|---|---|---|
payment.created | Payment initiation was created | POST /payments succeeds |
payment.completed | Payment was successfully executed | Funds transfer completes |
payment.failed | Payment execution failed | Insufficient funds, validation error, etc. |
Account Activity Events
Real-time notifications for account changes. Requires active account-access consent.
| Event Type | Description | When Triggered |
|---|---|---|
transaction.posted | New transaction posted to account | Debit or credit posts |
balance.updated | Account balance changed | After transaction posting |
Scheduled Payment Events
Notifications for standing orders and scheduled payments.
| Event Type | Description | When Triggered |
|---|---|---|
schedule.created | Scheduled payment was created | New standing order set up |
schedule.executed | Scheduled payment was executed | Recurring payment runs successfully |
schedule.failed | Scheduled payment failed | Recurring payment fails |
schedule.cancelled | Scheduled payment was cancelled | Standing order cancelled |
Webhook Payload
All webhook payloads follow a consistent structure:
{
"id": "evt_1234567890abcdef",
"type": "payment.completed",
"timestamp": "2024-01-15T10:30:00Z",
"resource_id": "pay_abc123",
"resource_type": "payment",
"data": {
"payment_id": "pay_abc123",
"status": "completed",
"amount": {
"amount": "100.00",
"currency": "GBP"
},
"creditor_account": {
"scheme": "SortCodeAccountNumber",
"identification": "608371-12345678"
}
}
}Payload Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique event identifier (for idempotency) |
type | string | Event type (e.g., payment.completed) |
timestamp | string | ISO 8601 timestamp when event occurred |
resource_id | string | ID of the affected resource |
resource_type | string | Type of resource (consent, payment, account) |
data | object | Event-specific payload data |
Request Headers
Each webhook request includes these headers for verification and debugging:
| Header | Description | Example |
|---|---|---|
Content-Type | Always application/json | application/json |
User-Agent | Identifies the webhook sender | HamsterBank-Webhooks/1.0 |
X-Webhook-ID | Your webhook subscription ID | whk_abc123 |
X-Event-ID | Unique event ID | evt_xyz789 |
X-Event-Type | Event type for routing | payment.completed |
X-Webhook-Signature | HMAC-SHA256 signature | a1b2c3d4... |
X-Webhook-Timestamp | Request timestamp | 2024-01-15T10:30:00Z |
Signature Verification
Every webhook request is signed using HMAC-SHA256. Always verify signatures to ensure requests originate from Hamster Bank and haven't been tampered with.
Security Critical
Never process webhook payloads without verifying the signature. Attackers could send fake events to manipulate your system.
Verification Steps
- Extract the signature from the X-Webhook-Signature header
- Compute HMAC-SHA256 of the raw request body using your webhook secret
- Compare signatures using a constant-time comparison function
- Reject requests with invalid signatures
Example (Go)
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifySignature(payload []byte, signature, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
expected := hex.EncodeToString(h.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Webhook-Signature")
body, _ := io.ReadAll(r.Body)
if !verifySignature(body, signature, os.Getenv("WEBHOOK_SECRET")) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Process the verified webhook...
w.WriteHeader(http.StatusOK)
}Example (Node.js)
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
app.post('/webhooks', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process the verified webhook...
res.status(200).send('OK');
});Retry Behavior
If your endpoint doesn't respond with a 2xx status code within 10 seconds, we'll retry delivery with exponential backoff:
| Attempt | Delay After Failure | Cumulative Time |
|---|---|---|
| 1 | — | Immediate |
| 2 | 30 seconds | 30 seconds |
| 3 | 1 minute | ~1.5 minutes |
| 4 | 2 minutes | ~3.5 minutes |
| 5 | 4 minutes | ~7.5 minutes |
After 5 failed attempts, the delivery is marked as abandoned. If a webhook consistently fails (10+ consecutive failures), it will be automatically disabled.
Best Practice
Respond to webhooks quickly (within 5 seconds) with a 200 status. Process the event asynchronously using a job queue if needed.
Best Practices
1. Respond Immediately
Return a 200 response as soon as you receive the webhook. Queue the event for async processing to avoid timeouts.
2. Handle Duplicates
Use the event ID for idempotency. Store processed event IDs and skip duplicates—we may retry even after successful delivery in rare cases.
3. Verify Signatures
Always verify the HMAC-SHA256 signature before processing. This prevents attackers from forging webhook events.
4. Handle Event Ordering
Events may arrive out of order. Use the timestamp field and resource state to handle this gracefully.
5. Monitor Your Endpoint
Set up alerting for webhook failures. Check the developer dashboard for delivery status and error details.
Error Handling
Your endpoint should return appropriate status codes:
| Status Code | Meaning | Our Action |
|---|---|---|
200-299 | Success | Mark as delivered |
4xx | Client error (bad request) | Retry (may be transient) |
5xx | Server error | Retry with backoff |
Timeout | No response within 10s | Retry with backoff |