Webhooks
Configure webhook endpoints for real-time event notifications including payload format, event types, security signatures, and retry policy.
Last updated: 2025-02-18
Webhooks
Certexi can send real-time HTTP notifications to your systems when events occur. Webhooks enable integration with external ERP systems, notification services, and custom automation.
Configuration
Register webhook endpoints through the admin dashboard or API:
POST /api/webhooks
Content-Type: application/json
{
"url": "https://your-system.com/webhooks/certexi",
"events": ["event.created", "transport_unit.stage_changed", "incident.created"],
"secret": "your-webhook-secret",
"active": true
}
| Field | Type | Description |
|---|---|---|
url | string | HTTPS endpoint to receive webhook payloads |
events | string[] | Event types to subscribe to |
secret | string | Shared secret for HMAC signature verification |
active | boolean | Enable or disable the webhook |
HTTPS Required
Webhook URLs must use HTTPS. HTTP endpoints are rejected to prevent credential exposure in transit.
Event Types
Workflow Events
| Event | Trigger |
|---|---|
event.created | Any event created in the system |
transport_unit.created | New transport unit registered |
transport_unit.stage_changed | Unit progressed to next workflow stage |
transport_unit.completed | Unit completed all workflow stages |
WHMS Events
| Event | Trigger |
|---|---|
whms.placement | Asset placed in a slot |
whms.removal | Asset removed from a slot |
whms.verification | Placement verified by supervisor |
whms.dispute | Placement disputed by supervisor |
Compliance Events
| Event | Trigger |
|---|---|
incident.created | New incident reported |
incident.resolved | Incident marked as resolved |
audit.report_generated | Compliance report generated |
IoT Events
| Event | Trigger |
|---|---|
motion.detected | Motion detected on a camera |
scale.reading | New scale reading recorded |
Payload Format
All webhook payloads follow a consistent structure:
{
"id": "evt_abc123",
"type": "transport_unit.stage_changed",
"timestamp": "2025-01-15T14:30:00Z",
"data": {
"transportUnitId": "TU-2024-00042",
"fromStage": "bascula",
"toStage": "supervisor",
"operator": "op-123",
"evidence": {
"photos": 2,
"scaleReading": 24500
}
},
"metadata": {
"warehouseId": 1,
"siteId": "site-main",
"version": "2.0.0"
}
}
Security
HMAC Signature Verification
Every webhook request includes a signature header for payload verification:
X-Certexi-Signature: sha256=<hmac_hex>
Verify the signature in your endpoint:
import crypto from 'crypto';
function verifyWebhook(
payload: string,
signature: string,
secret: string
): boolean {
const expected =
'sha256=' +
crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Always Verify Signatures
Never process webhook payloads without verifying the HMAC signature. This prevents replay attacks and payload tampering.
Additional Headers
| Header | Description |
|---|---|
X-Certexi-Event | Event type (e.g., transport_unit.stage_changed) |
X-Certexi-Delivery | Unique delivery ID for deduplication |
X-Certexi-Timestamp | ISO 8601 timestamp of the event |
X-Certexi-Signature | HMAC-SHA256 signature |
Retry Policy
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
A delivery is considered failed if:
- The endpoint returns a non-2xx status code
- The connection times out (30-second timeout)
- The endpoint is unreachable
After 6 failed attempts, the webhook is marked as failing and an alert is sent to the admin dashboard. Webhooks are automatically disabled after 100 consecutive failures.
Webhook Payload Card
<Card className="w-full"> <CardHeader className="pb-2"> <div className="flex items-center justify-between"> <CardTitle className="text-sm font-mono">transport_unit.stage_changed</CardTitle> <Badge className="bg-green-500 text-white text-[10px]">200 OK</Badge> </div> <CardDescription>Delivered 2.3s ago — Attempt 1/6</CardDescription> </CardHeader> <CardContent className="space-y-2"> <div className="bg-muted/50 rounded p-2 font-mono text-[11px] leading-relaxed"> <div>{"{"}</div> <div className="pl-3">"type": "transport_unit.stage_changed",</div> <div className="pl-3">"data": {"{"}</div> <div className="pl-6">"transportUnitId": "TU-2025-00042",</div> <div className="pl-6">"fromStage": "bascula",</div> <div className="pl-6">"toStage": "supervisor"</div> <div className="pl-3">{"}"}</div> <div>{"}"}</div> </div> <div className="flex items-center gap-2 text-[10px] text-muted-foreground"> <span>X-Certexi-Signature: sha256:a4f2e8...</span> </div> </CardContent> </Card>
Testing
Use the webhook test endpoint to send a sample payload:
POST /api/webhooks/:id/test
This sends a webhook.test event to the registered URL without affecting production data.