Webhooks API
Configure webhook endpoints to receive real-time notifications for document, shipment, and compliance events.
Webhooks API
CargoLint webhooks deliver real-time notifications for document, shipment, and compliance events. Instead of polling the API, webhooks push event data directly to your application as processing happens.
Webhook access is available on Business and Enterprise plans.
Authentication: Webhook management endpoints require an Owner or Admin user session, so they authenticate with a JWT (
Authorization: Bearer {token}), not an API key. API keys do not carry a role and will receive403on these endpoints.
Create Webhook
Register a new webhook endpoint.
Endpoint
POST /webhooks
Request Schema
{
"url": "https://example.com/webhooks/cargolint",
"events": [
"document.completed",
"shipment.evaluated"
]
}
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
| url | String | Yes | HTTPS endpoint URL to receive webhooks |
| events | String[] | Yes | Event types to subscribe to (see Supported Event Types) |
Example Request
curl -X POST "https://api.cargolint.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/cargolint",
"events": ["document.completed", "shipment.evaluated"]
}'
Response Schema
The response includes the signing secret. This is the only time the secret is returned - store it now. It is never shown again (rotate it if lost).
{
"id": "3f1c1b2a-...",
"url": "https://example.com/webhooks/cargolint",
"events": ["document.completed", "shipment.evaluated"],
"isActive": true,
"createdAt": "2026-06-17T10:30:00Z",
"updatedAt": null,
"secret": "Base64SigningSecretShownOnceProtectThisValue=="
}
List Webhooks
GET /webhooks
Returns all webhooks for your organization. The signing secret is not included.
curl "https://api.cargolint.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Get Webhook
GET /webhooks/{id}
Returns a single webhook (without the secret).
Update Webhook
PUT /webhooks/{id}
Request Schema
All fields are optional; only the fields you send are changed.
{
"url": "https://example.com/webhooks/cargolint-v2",
"events": ["document.completed", "compliance.completed"],
"isActive": true
}
| Field | Type | Description |
|---|---|---|
| url | String | New endpoint URL |
| events | String[] | Replacement list of subscribed events |
| isActive | Boolean | Enable or disable the webhook |
curl -X PUT "https://api.cargolint.com/api/v1/webhooks/{id}" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "events": ["document.completed", "compliance.completed"] }'
Delete Webhook
DELETE /webhooks/{id}
curl -X DELETE "https://api.cargolint.com/api/v1/webhooks/{id}" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Rotate Signing Secret
Generate a new signing secret. The response returns the new plaintext secret once; the previous secret stops being valid immediately. Use this if a secret is lost or may be compromised - it preserves the webhook and its delivery history.
Endpoint
POST /webhooks/{id}/rotate-secret
Example Request
curl -X POST "https://api.cargolint.com/api/v1/webhooks/{id}/rotate-secret" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
The response has the same shape as the create response, including the new secret.
Send Test Payload
Queue a webhook.test delivery to verify your endpoint is reachable.
POST /webhooks/{id}/test
curl -X POST "https://api.cargolint.com/api/v1/webhooks/{id}/test" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Webhook Delivery History
GET /webhooks/{id}/deliveries
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | Integer | 1 | Page number (1-based) |
| pageSize | Integer | 50 | Items per page (max 100) |
Response Schema
{
"items": [
{
"id": "...",
"event": "document.completed",
"status": "Success",
"attempts": 1,
"lastAttemptAt": "2026-06-17T11:45:02Z",
"lastError": null,
"responseStatusCode": 200,
"createdAt": "2026-06-17T11:45:00Z"
}
],
"totalCount": 1,
"page": 1,
"pageSize": 50,
"totalPages": 1
}
Delivery status is one of Pending, Success, or Failed.
Supported Event Types
Document events
| Event | Description |
|---|---|
| document.uploaded | Document received and queued for processing |
| document.pending_approval | Extraction completed with high confidence; awaiting approval |
| document.requires_review | Extraction completed with low confidence, or a shipment-consistency finding sent the document back for review |
| document.failed | Extraction failed (non-transient) |
| document.reviewed | A reviewer submitted corrections |
| document.completed | Document approved and finalized |
| document.generated | A document was generated from another (e.g. a packing list from an invoice) |
| document.cloned | A document was cloned |
| document.skipped | A reviewer deferred the document |
Shipment events
| Event | Description |
|---|---|
| shipment.created | A new shipment was created |
| shipment.document_linked | A document was attached to a shipment |
| shipment.evaluated | Cross-document consistency evaluation completed (payload includes overallStatus) |
| shipment.settled | A shipment was finalized |
Compliance events
| Event | Description |
|---|---|
| compliance.completed | A single-document compliance check finished (payload includes overallStatus) |
| compliance.warning | Deprecated. Fires alongside compliance.completed only when the status is Warning or Error. Prefer compliance.completed. |
webhook.test is also delivered when you use Send Test Payload.
Webhook Payload Format
Every delivery body has the same envelope. The data object is flat and its fields depend on the event.
{
"event": "document.completed",
"timestamp": "2026-06-17T11:45:00Z",
"data": {
"documentId": "...",
"fileName": "invoice.pdf",
"status": "Completed",
"completedVia": "review"
}
}
Example shipment.evaluated payload:
{
"event": "shipment.evaluated",
"timestamp": "2026-06-17T11:46:00Z",
"data": {
"shipmentId": "...",
"reference": "Shipment-2026-06-17-001",
"overallStatus": "Warning",
"reportId": "...",
"ruleResultCount": 3
}
}
Identifiers such as the delivery id are sent as headers (below), not in the body.
Delivery Headers
Each request includes:
| Header | Description |
|---|---|
| X-CargoLint-Signature | v1={hex} - HMAC-SHA256 signature (see below) |
| X-CargoLint-Timestamp | Unix timestamp (seconds) used in the signature |
| X-CargoLint-Event | The event type |
| X-CargoLint-DeliveryId | Unique delivery id (use for idempotency) |
Signature Verification
The signature is HMAC-SHA256 over the string "{timestamp}.{payload}", where timestamp is the value of the X-CargoLint-Timestamp header and payload is the raw request body.
Your signing secret is base64-encoded - decode it to raw bytes to use as the HMAC key. The signature is sent hex-encoded in the X-CargoLint-Signature header, prefixed with v1=.
import base64
import hashlib
import hmac
def verify_signature(payload: str, signature_header: str, timestamp_header: str, secret: str) -> bool:
signed = f"{timestamp_header}.{payload}".encode()
expected = hmac.new(
base64.b64decode(secret), # secret is base64; the HMAC key is the decoded bytes
signed,
hashlib.sha256,
).hexdigest()
provided = signature_header.split("v1=", 1)[-1]
return hmac.compare_digest(expected, provided)
Security: Always use
hmac.compare_digest()(constant-time comparison) - never==, which is vulnerable to timing attacks. To prevent replay, also reject deliveries whoseX-CargoLint-Timestampis outside a tolerance window (for example, more than five minutes from now).
Delivery, Retries, and Failures
- Pending deliveries are dispatched within a few seconds.
- Each attempt has a 10-second timeout.
- A non-2xx response or network error is retried up to 3 attempts with a backoff of 5s, 15s, then 45s.
- After the final failed attempt the delivery is marked
Failed(terminal). The webhook itself stays active - it is not auto-disabled - so future events still attempt delivery. - Inspect outcomes via Delivery History.
Best Practices
- Verify the signature before processing every delivery.
- Respond with a 2xx status within 10 seconds; do heavy work asynchronously.
- Deduplicate using
X-CargoLint-DeliveryId- retries reuse the same id. - Store the signing secret when you create the webhook; rotate it with Rotate Signing Secret if it is lost or exposed.
- Subscribe only to the events you need.