Back to home

Webhooks

Receive real-time notifications when your bank validations are complete.

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.

1

Register Your URL

Configure your endpoint URL via the API. Specify the events you want to subscribe to.

2

Automatic Delivery

For each matching event, BankValidor sends an HTTP POST to your URL with the validation details in JSON format.

3

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.

EventDescription
validation.completedEmitted when a single validation (US, UK, SWIFT, IBAN) completes successfully.
validation.failedEmitted when a single validation fails (format error, registry not found).
bulk.completedEmitted when bulk CSV file processing is complete. Contains the download link for the result file.
bulk.failedEmitted 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.

Example payload (validation.completed)
{
  "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 event
  • api_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...
Python
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)
Node.js
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

POST /api/v1/webhooks
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

GET /api/v1/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

DELETE /api/v1/webhooks/:id
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.

AttemptDelayTimeout
1st attemptImmediate30s
2nd attempt5 minutes30s
3rd attempt30 minutes30s

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

1

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.

2

Process asynchronously

Save the payload and process it in the background. This avoids timeouts and ensures BankValidor receives your acknowledgment.

3

Implement idempotency

Use the event_id to detect and ignore duplicates. During retries, the same event may be sent multiple times.

Python (FastAPI)
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"})
Node.js (Express)
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