Skip to Content

Webhooks

Webhooks (called Callbacks in the API) allow you to receive real-time HTTP POST notifications when specific events occur in Borderbolt — such as declaration status changes, document generation, or transit updates.

The webhook management API is documented under Callbacks API. This page covers the webhook payload format and how to verify and handle incoming events.


Webhook Events

Declaration Status Events

EventTrigger
declaration.submittedDeclaration submitted to customs
declaration.acceptedDeclaration accepted by customs (MRN assigned)
declaration.rejectedDeclaration rejected by customs
declaration.releasedGoods released by customs
declaration.controlControl notification received
declaration.invalidatedDeclaration invalidated
declaration.errorSubmission error occurred

Document Events

EventTrigger
declaration.document.utbUTB (Invitation to Pay) generated
declaration.document.sadSAD (Single Administrative Document) generated
declaration.document.iiaaIIAA certificate generated

Transit Events

EventTrigger
transit.submittedTransit departure submitted (IE015)
transit.acceptedTransit accepted, MRN assigned (IE029)
transit.rejectedTransit rejected (IE016)
transit.arrivedArrival notification received (IE043)
transit.completedTransit completed successfully
transit.enquiryEnquiry received from customs (IE140)
transit.invalidatedTransit invalidated

Webhook Payload

All webhook payloads follow this structure:

{ "event": "declaration.released", "timestamp": "2026-03-25T16:00:00.000000Z", "data": { } }

Declaration Event Data

{ "event": "declaration.released", "timestamp": "2026-03-25T16:00:00.000000Z", "data": { "id": 12345, "reference": "IMP-2026-001", "mrn": "26NL123456789012345", "status": "REL", "old_status": "ACC", "customer_code": "CUST001", "customer_name": "Example B.V.", "declaration_type": "H1", "total_items": 3, "total_invoice_value": 15000.00, "currency": "EUR", "submitted_at": "2026-03-25T11:00:00.000000Z", "updated_at": "2026-03-25T16:00:00.000000Z" } }

Document Event Data

{ "event": "declaration.document.utb", "timestamp": "2026-03-25T16:05:00.000000Z", "data": { "id": 12345, "reference": "IMP-2026-001", "mrn": "26NL123456789012345", "document_type": "utb", "generated_at": "2026-03-25T16:05:00.000000Z" } }

Transit Event Data

{ "event": "transit.accepted", "timestamp": "2026-03-25T09:17:30.000000Z", "data": { "id": 5001, "lrn": "26NL00001234567890123", "mrn": "26NL12345678901234567", "status": "ACC", "old_status": "NEW", "customer_code": "CUST001", "transit_type": "T1", "office_of_departure": "NL000396", "office_of_destination": "BE123456", "submitted_at": "2026-03-25T09:15:00.000000Z" } }

Signature Verification

All webhook requests include an HMAC-SHA256 signature in the X-Borderbolt-Signature header. Always verify this signature to ensure the request came from Borderbolt.

  1. Extract the signature from X-Borderbolt-Signature
  2. Compute HMAC-SHA256 of the raw request body using your callback secret
  3. Compare using a constant-time comparison function

PHP

$payload = file_get_contents('php://input'); $receivedSignature = $_SERVER['HTTP_X_BORDERBOLT_SIGNATURE'] ?? ''; $secret = 'your-callback-secret'; $computedSignature = hash_hmac('sha256', $payload, $secret); if (!hash_equals($computedSignature, $receivedSignature)) { http_response_code(401); exit('Invalid signature'); } $event = json_decode($payload, true); // Process event...

Python

import hmac import hashlib from flask import Flask, request app = Flask(__name__) @app.route('/webhooks/borderbolt', methods=['POST']) def webhook(): received = request.headers.get('X-Borderbolt-Signature', '') secret = b'your-callback-secret' computed = hmac.new(secret, request.data, hashlib.sha256).hexdigest() if not hmac.compare_digest(received, computed): return 'Invalid signature', 401 event = request.json # Process event... return 'OK', 200

Response Requirements

Your webhook endpoint must:

  1. Respond within 5 seconds — process events asynchronously if needed
  2. Return HTTP 2xx — any 2xx response confirms receipt
  3. Handle duplicate deliveries — use timestamp + data.id for idempotency

Retries: If your endpoint returns HTTP 4xx/5xx or times out, Borderbolt retries up to 3 times with exponential backoff (1 min, 5 min, 15 min).


Best Practices

Verify signatures — always, to prevent spoofed requests.

Queue for async processing — respond immediately with 200, process in the background:

Queue::push(new ProcessBorderboltWebhook($event)); return response('OK', 200);

Idempotency — check whether the event was already processed before acting:

if (alreadyProcessed($event['data']['id'], $event['event'])) { return response('OK', 200); }

Managing Callbacks

Register, list, delete, regenerate secrets, and send test events via the Callbacks API.


Next Steps

Last updated on