Business Pack Feature
Webhooks are available exclusively with the Business Pack. They allow you to receive automatic notifications without continuously polling the API.
How It Works
Webhooks allow BankValidor to send HTTP POST notifications to your server when an event occurs.
Register Your URL
Configure your endpoint URL via the API. Specify the events you want to subscribe to.
Automatic Delivery
For each matching event, BankValidor sends an HTTP POST to your URL with the validation details in JSON format.
Retry Policy
If your server doesn't respond with a 2xx code, BankValidor automatically retries with exponential backoff (up to 3 attempts).
Available Events
BankValidor emits the following events. Select those you want to subscribe to when configuring your webhook.
| Event | Description |
|---|---|
| validation.completed | Emitted when a single validation (US, UK, SWIFT, IBAN) completes successfully. |
| validation.failed | Emitted when a single validation fails (format error, registry not found). |
| bulk.completed | Emitted when bulk CSV file processing is complete. Contains the download link for the result file. |
| bulk.failed | Emitted when CSV file processing fails (format error, timeout, corrupted file). |
Payload Format
Each webhook notification contains a structured JSON payload with event information and validation data.
{
"event_id": "evt_a1b2c3d4e5f6",
"event_type": "validation.completed",
"timestamp": "2025-01-15T14:30:00Z",
"api_version": "v1",
"data": {
"validation_id": "val_x9y8z7w6",
"type": "swift",
"input": "DEUTDEFF500",
"status": "VALID",
"confidence": "HIGH",
"bank_info": {
"name": "Deutsche Bank AG",
"city": "Frankfurt am Main",
"country": "DE"
},
"validation_details": {
"syntax": true,
"checksum": true,
"registry_match": true
}
}
}Payload Fields
event_idUnique event identifier (for deduplication / idempotency)event_typeEvent type (validation.completed, validation.failed, bulk.completed, bulk.failed)timestampISO 8601 timestamp of the eventapi_versionAPI version used (currently v1)dataObject containing validation details (type, input, status, confidence, bank_info)
Security
Each webhook notification is signed with your secret to ensure payload authenticity and integrity.
Signature Header
BankValidor signs each payload with HMAC-SHA256 using your webhook secret. Verify this signature server-side to ensure the notification comes from BankValidor.
X-Webhook-Signature: sha256=a1b2c3d4e5f6...import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
"""Verify the HMAC-SHA256 signature of a webhook payload."""
expected = hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
received = signature.replace("sha256=", "")
return hmac.compare_digest(expected, received)
# Usage in your endpoint:
# signature = request.headers["X-Webhook-Signature"]
# is_valid = verify_webhook_signature(request.body, signature, WEBHOOK_SECRET)const crypto = require("crypto");
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf-8")
.digest("hex");
const received = signature.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex")
);
}
// Usage in your Express handler:
// const signature = req.headers["x-webhook-signature"];
// const isValid = verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET);Configuration
Manage your webhooks via the REST API. You can create, list, and delete webhooks.
Register a Webhook
curl -X POST https://bankvalidor.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/bank-validator",
"events": ["validation.completed", "validation.failed"],
"secret": "your_webhook_secret"
}'
# Response:
{
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/bank-validator",
"events": ["validation.completed", "validation.failed"],
"active": true,
"created_at": "2025-01-15T10:00:00Z"
}List Webhooks
curl https://bankvalidor.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"
# Response:
{
"webhooks": [
{
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/bank-validator",
"events": ["validation.completed", "validation.failed"],
"active": true,
"created_at": "2025-01-15T10:00:00Z"
}
]
}Delete a Webhook
curl -X DELETE https://bankvalidor.com/api/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
# Response:
{
"deleted": true,
"id": "wh_abc123"
}Retry Policy
In case of delivery failure (timeout, HTTP 5xx error), BankValidor automatically retries with increasing delay.
| Attempt | Delay | Timeout |
|---|---|---|
| 1st attempt | Immediate | 30s |
| 2nd attempt | 5 minutes | 30s |
| 3rd attempt | 30 minutes | 30s |
After 3 failures
If all 3 attempts fail, the event is marked as undelivered. You can check failed events via the GET /api/v1/webhooks/failures endpoint and replay them manually.
Best Practices
Respond immediately with a 200
Your endpoint should return an HTTP 200 code as quickly as possible. Any heavy processing should be done asynchronously after the response.
Process asynchronously
Save the payload and process it in the background. This avoids timeouts and ensures BankValidor receives your acknowledgment.
Implement idempotency
Use the event_id to detect and ignore duplicates. During retries, the same event may be sent multiple times.
from fastapi import FastAPI, Request, BackgroundTasks
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/webhooks/bank-validator")
async def handle_webhook(request: Request, bg: BackgroundTasks):
# 1. Respond 200 immediately
payload = await request.json()
event_id = payload["event_id"]
# 2. Check idempotency (skip if already processed)
if await is_already_processed(event_id):
return JSONResponse({"status": "already_processed"})
# 3. Process asynchronously
bg.add_task(process_validation_event, payload)
return JSONResponse({"status": "accepted"})const express = require("express");
const app = express();
app.post("/webhooks/bank-validator", express.json(), async (req, res) => {
const { event_id } = req.body;
// 1. Respond 200 immediately
res.status(200).json({ status: "accepted" });
// 2. Check idempotency (skip if already processed)
const alreadyProcessed = await checkIfProcessed(event_id);
if (alreadyProcessed) return;
// 3. Process asynchronously
processValidationEvent(req.body).catch(console.error);
});Related Documentation
BankValidor — Webhooks