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
| Event | Trigger |
|---|---|
declaration.submitted | Declaration submitted to customs |
declaration.accepted | Declaration accepted by customs (MRN assigned) |
declaration.rejected | Declaration rejected by customs |
declaration.released | Goods released by customs |
declaration.control | Control notification received |
declaration.invalidated | Declaration invalidated |
declaration.error | Submission error occurred |
Document Events
| Event | Trigger |
|---|---|
declaration.document.utb | UTB (Invitation to Pay) generated |
declaration.document.sad | SAD (Single Administrative Document) generated |
declaration.document.iiaa | IIAA certificate generated |
Transit Events
| Event | Trigger |
|---|---|
transit.submitted | Transit departure submitted (IE015) |
transit.accepted | Transit accepted, MRN assigned (IE029) |
transit.rejected | Transit rejected (IE016) |
transit.arrived | Arrival notification received (IE043) |
transit.completed | Transit completed successfully |
transit.enquiry | Enquiry received from customs (IE140) |
transit.invalidated | Transit 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.
- Extract the signature from
X-Borderbolt-Signature - Compute
HMAC-SHA256of the raw request body using your callback secret - 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', 200Response Requirements
Your webhook endpoint must:
- Respond within 5 seconds — process events asynchronously if needed
- Return HTTP 2xx — any 2xx response confirms receipt
- Handle duplicate deliveries — use
timestamp+data.idfor 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
- Callbacks API — Register and manage webhook subscriptions
- Declarations API — Declaration data structure
- Transit API — Transit data structure