Wocart

Carrier webhooks

Carriers push delivery updates to Wocart. The server verifies the signature, stores the events in an inbox, answers the carrier immediately, and processes the events after the response. Your integration reads the resulting shipment state through the API.

To receive your own store's events on your server, read Webhooks instead.

Inbound webhooks exist for Yalidine and ZR Express. NOEST, the EcoTrack-platform companies and Guepex send none: their shipments are synced every five minutes, and on demand with POST /shipments/{id}/sync or POST /delivery-companies/{id}/sync-shipments.

Endpoints

CarrierEndpointDelivery shapeSignature
YalidinePOST /webhooks/yalidineA batch of events of one typeHMAC-SHA256 in x-yalidine-signature
YalidineGET /webhooks/yalidineCRC challengeEchoes crc_token as plain text
ZR ExpressPOST /webhooks/zr_expressOne Svix event per requestSvix HMAC-SHA256

The paths are the URLs the carriers hold. Do not put them behind auth: the carriers cannot send your API key. The signature is the authentication.

How intake works

  1. Carrier to Wocart

    The carrier sends a signed POST to /webhooks/yalidine or /webhooks/zr_express.

  2. Verify and split

    Wocart checks the signature against the company's secret and splits the carrier envelope into individual events.

  3. Store in the inbox

    Each event goes into webhook_events, keyed by its provider event ID.

  4. Answer the carrier

    Wocart returns 200 {"received":true} without waiting for processing.

  5. Process after the response

    A few seconds later the events queue wakes the drain, which applies each event's shipment milestones and records their Business Events. The five-minute schedule runs the same drain, so an event a lost wake missed is still processed.

The carrier gets its 200 as soon as the events are stored. Processing failures do not fail the webhook; the drain retries them on the inbox's schedule. Yalidine requires an answer within 10 seconds and retries a failed delivery seven times over 40 hours, so this split matters.

Set up a webhook

Both flows start on the delivery company's credentials page in the dashboard.

Yalidine

  1. Open the company's credentials page and copy the receiver URL from the webhook setup card.
  2. In Yalidine's Webhooks Dashboard, create an endpoint with that URL.
  3. Subscribe to the event types you need. Yalidine validates the URL with the CRC challenge.
  4. Copy the signing secret from Yalidine back into the card and save it. Store the same value in your secret manager.

ZR Express

  1. Open the company's credentials page and choose Register webhook.
  2. The server registers the endpoint with ZR Express and fetches the signing secret for you.
  3. The card shows the endpoint as registered. Use Unregister to remove it.

Send a test event from the carrier and confirm the delivery company receives it.

A webhook needs its secret. A delivery no company's secret verifies is refused with 401 and never stored: what a carrier reports becomes a Business Event that merchants' systems and customers may be told about, so it must be proven to come from the carrier. If ZR Express does not give its signing secret, the registration is undone and nothing is kept; register again.

Signature verification

Yalidine

Yalidine signs the raw request body with HMAC-SHA256 using the company's webhook secret. The signature arrives in x-yalidine-signature (or x_yalidine_signature). The server accepts lowercase hex, uppercase hex and base64 representations, and compares in constant time.

The GET challenge is separate: when Yalidine sends subscribe and crc_token query parameters, the server echoes the token as plain text. This lets Yalidine verify the URL before it starts sending events.

ZR Express

ZR Express uses Svix. The signed content is:

Code
{svix-id}.{svix-timestamp}.{rawBody}

The signature arrives in svix-signature, and the server accepts any of the space-separated v1,<base64> entries. A missing svix-id, svix-timestamp or svix-signature is rejected.

Idempotency and ordering

  • Yalidine batches many events in one request. Each event has its own event_id, which is the inbox key.
  • ZR Express sends one event per request. svix-id is the inbox key.
  • Duplicate deliveries are ignored by the inbox, so a carrier retry cannot apply the same event twice.
  • Events can arrive out of order. The server applies shipment milestones by their event timestamp, not by arrival order.

Local development

Carriers need a public HTTPS URL. Expose your local Worker with a tunnel you trust, then configure that origin with the carrier. For Yalidine, create the endpoint at <tunnel-origin>/webhooks/yalidine and save its secret in the company's webhook card. For ZR Express, send the tunnel origin as X-Worker-URL when you register, so the endpoint it creates points at the tunnel instead of localhost.

The local receiver is still http://localhost:8787/webhooks/yalidine or /webhooks/zr_express, so you can replay a signed payload with curl while developing:

TerminalCode
curl -X POST http://localhost:8787/webhooks/yalidine \ -H "Content-Type: application/json" \ -H "x-yalidine-signature: <hex hmac of the raw body>" \ --data-binary @event.json