Skip to main content
Shipment webhooks send signed JSON POST requests when OVRSEA observes a change to a shipment’s client-visible status.

Configure your endpoint

In Hermes, open API Integration → API → Shipment webhooks. For Lyseo/Fatton accounts without the API Integration menu, open Account settings → Shipment webhooks. An account owner or admin can save, replace, remove and test one URL for their own client account. The URL receives status events for that client’s shipments across ocean, air, rail and truck. Configuration requires direct access to that exact account; access through a parent, child or shared account does not grant this permission. OneChain and logistic-agent accounts cannot configure these webhooks. Accounts with an existing bespoke webhook integration must arrange its migration first. Use a publicly reachable HTTPS URL, for example https://integrations.example.com/shipment-webhooks. Credentials in the URL, query strings and fragments are rejected. Loopback, private, link-local, metadata and reserved IP addresses are blocked, including addresses returned by DNS. Redirects are not followed; configure the final destination. Saving the URL makes it eligible for delivery immediately. Saving it sends no historical snapshot or backfill. Replacing or removing it cancels pending work for the former configuration, although a delivery attempt already in progress can finish at the former URL.

Keep the signing secret

Creation and secret rotation show the new signing secret once. Copy it directly to your receiver’s secret store. Reading the configuration or replacing its URL does not reveal or rotate the secret. If you lose it, rotate it. The signing secret is separate from your REST API token; keep it out of URLs and logs. Rotation takes effect for future signing attempts, including retries. Each attempt uses the current secret. A request signed before rotation may still arrive with the previous key; coordinate receiver updates and, if needed, keep a short, bounded verification overlap for those requests. Rotation does not cancel pending deliveries.

Event and payload

The supported event type is shipment.status_changed, with this V1 payload:
Every field is required. V1 contains no tracking details or previous status. Cancellation uses status: "cancelled" in the same event. Quotation-only states are excluded. Status observations can fill intermediate progression stages, but they do not form a complete history of business transitions. shipmentId is not a file reference or the UUID used by the REST shipment and tracking routes. Those routes do not accept this numeric internal ID.

Verify each request

Requests use Content-Type: application/json and these four webhook headers: Compute HMAC-SHA256 with the signing secret’s UTF-8 bytes as the key. Use the secret’s text as shown, even though it looks hexadecimal; do not hex-decode it. The signed message is the UTF-8 header prefix followed by the exact raw body bytes:
Capture the body before JSON parsing. Do not reserialize JSON, change whitespace or decode and re-encode the body before verifying. Validate the header formats, compare digests in constant time, then parse the authenticated body and check that its eventId matches the header. The signed mode must be validated too. We recommend rejecting timestamps more than five minutes in the past or future, with synchronized receiver clocks. This tolerance is a receiver recommendation, not a sender policy. Use the header timestamp, not emittedAt; retries have a fresh timestamp and signature for the same event ID and body.

Node.js verification example

This Node.js example uses Zod for payload validation (npm install zod). Pass a Headers instance and a Buffer containing the unmodified request body. In a Node HTTP handler, capture bytes with buffer(request) from node:stream/consumers before any JSON middleware; in a handler using a Web Request, use Buffer.from(await request.arrayBuffer()). Configure request size and read-time limits at your ingress or HTTP server.
Validation failures must stop processing. After verification, route test to an acknowledgement without shipment updates, notifications or other business effects. For live, complete the durable processing described below before returning 2xx.

Acknowledge and handle duplicates

Store processed eventId values durably for this integration. Enforce uniqueness and commit the event ID with its business changes in the same database transaction. Concurrent requests must not apply the effect twice. An in-memory set does not protect against restarts or multiple receiver instances. If the effect calls another system, give that system a durable idempotency key based on eventId before acknowledging completion. Authenticate every attempt, including duplicates. An authenticated retry of an already completed event should receive 2xx again without repeating its effect. Do not permanently reject an event ID just because it was seen before: the previous acknowledgement may have been lost. A stale or unauthenticated replay must not trigger processing. If processing fails, leave it retryable and return a non-2xx response rather than recording the event as completed. Any 2xx acknowledges delivery; a small empty 204 response is sufficient. Redirects, other status codes, timeouts and network failures do not acknowledge it. DNS resolution and HTTP share a ten-second attempt budget, and the response body is capped at 64 KiB. Live deliveries have at most four attempts: the initial attempt and retries eligible after 60, 600 and 3600 seconds from the preceding attempt’s completion. Polling and backlog can delay them further, and a failed signing step can consume an attempt. Delivery is best effort: ordering, capture of every status transition and exactly-once receipt are not guaranteed. Handle duplicates and avoid assuming that arrival order is shipment progression order.

Test your receiver

An owner or admin can send a test from Shipment webhooks after saving the URL. Enter the shipment file reference shown in Hermes, for example BB-ABCD-3. Hermes resolves it to the internal numeric shipment ID used by the test request. The shipment must belong to the same exact client account; visibility through a parent, child or shared account does not grant testing permission. A REST shipment UUID is not a file reference. Choose a transport shipment in a supported status; quotation-only states cannot be tested. A test sends the selected shipment’s current status to your configured URL with the signed X-Ovrsea-Webhook-Mode: test header. Each test has a fresh event ID, uses the same verification steps as a live request and has one synchronous attempt with no automatic retry. It does not change the shipment or activate the endpoint. Your receiver must acknowledge it without business effects. The test result shows success or failure, attempt time, HTTP status when available, duration and a sanitized error category. Check these alongside your receiver’s acknowledgement and durable processing records; the remote response body is not shown.