2026 customs changesWhat it means

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 receive 403 on 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)

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 whose X-CargoLint-Timestamp is 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.