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

FieldTypeRequiredDescription
urlStringYesHTTPS endpoint URL to receive webhooks
eventsString[]YesEvent 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
}
FieldTypeDescription
urlStringNew endpoint URL
eventsString[]Replacement list of subscribed events
isActiveBooleanEnable 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

ParameterTypeDefaultDescription
pageInteger1Page number (1-based)
pageSizeInteger50Items 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

EventDescription
document.uploadedDocument received and queued for processing
document.pending_approvalExtraction completed with high confidence; awaiting approval
document.requires_reviewExtraction completed with low confidence, or a shipment-consistency finding sent the document back for review
document.failedExtraction failed (non-transient)
document.reviewedA reviewer submitted corrections
document.completedDocument approved and finalized
document.generatedA document was generated from another (e.g. a packing list from an invoice)
document.clonedA document was cloned
document.skippedA reviewer deferred the document

Shipment events

EventDescription
shipment.createdA new shipment was created
shipment.document_linkedA document was attached to a shipment
shipment.evaluatedCross-document consistency evaluation completed (payload includes overallStatus)
shipment.settledA shipment was finalized

Compliance events

EventDescription
compliance.completedA single-document compliance check finished (payload includes overallStatus)
compliance.warningDeprecated. 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:

HeaderDescription
X-CargoLint-Signaturev1={hex} - HMAC-SHA256 signature (see below)
X-CargoLint-TimestampUnix timestamp (seconds) used in the signature
X-CargoLint-EventThe event type
X-CargoLint-DeliveryIdUnique 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.