Swiftazu

V1 · Guides

Webhooks

A webhook is a message the platform sends to your server when something happens to one of your orders or your account, so you do not have to keep asking.

Setting it up

Webhooks are set on the Application, in the merchant portal: the address to send to (HTTPS only) and the events you want. There is no API endpoint that changes them. That is deliberate, so an integration cannot quietly redirect its own events.

When you add a webhook address, the portal shows a signing secret once. Keep it: you need it to check each message.

What you receive

Each message is an HTTPS POST with a JSON body: the event name, the time, and the data about what happened.

POST
POST /your/webhook/address HTTP/1.1
Content-Type: application/json
X-Swiftazu-Event: order.status_changed
X-Swiftazu-Signature: v1=5d6c0f2a8a1b4c7e9f0a3b2c1d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6
X-Swiftazu-Delivery-Attempt: 1

{
  "event": "order.status_changed",
  "occurredAt": "2026-10-02T02:30:52.216Z",
  "data": {
    "id": "dffcd25d-6eb3-4b90-868c-6deca160ad74",
    "orderId": "SWZORDYFSADMR",
    "awbNumber": "FLC0000001042",
    "merchantReference": "WEB-10482",
    "status": { "code": "picked_up", "name": "Picked Up" },
    "previousStatus": "pickup_assigned",
    "reason": null
  }
}

Headers

FieldTypeDescription
X-Swiftazu-EventrequiredstringThe event name.
X-Swiftazu-SignaturerequiredstringThe signature of the body (see below).
X-Swiftazu-Delivery-Attemptrequirednumber1 for the first try, then 2, 3, 4 on retries.

Checking the signature

Every message is signed so you can be sure it came from the platform and was not changed on the way. The signature is an HMAC-SHA256 of the exact body bytes, made with your signing secret, written as v1= followed by the hex digest.

Compute the same value over the raw body you received (before parsing the JSON) and compare. If it differs, ignore the message.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the exact bytes received, BEFORE JSON.parse.
export function isFromSwiftazu(rawBody, signatureHeader, signingSecret) {
  const expected = "v1=" + createHmac("sha256", signingSecret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Answering

Answer with any 2xx status within 10 seconds. Do the real work after answering. The same event can arrive more than once, so make your handler safe to run twice.

Retries

A failed delivery is retried three times: after 1 minute, 10 minutes and 60 minutes. Every try is kept in the delivery log on the Application, with what was sent and what your server answered. You can replay any delivery from there.

If every delivery to your address fails for 24 hours in a row, the address is switched off. You get a warning by email at 20 hours. Switch it back on from the Application once your server is fixed.

Demo mode

Demo orders send the same webhooks as live orders, so you can test your handler fully before going live.

Events

EventSent when
order.createdAn order is created.
order.dispatchedAn order is dispatched and its air waybill is issued.
order.status_changedAny status or sub-status change is accepted.
order.received_at_hubA hub scans the shipment in.
manifest.closedA hub closes an outbound manifest that holds your shipments.
manifest.receivedA hub receives a manifest that holds your shipments, with any discrepancy.
order.picked_upCollection from your pickup address is confirmed.
order.customs_eventA customs status or document event happens.
order.out_for_deliveryA delivery attempt begins.
order.deliveredDelivery is completed with proof.
order.delivery_failedA delivery attempt fails.
order.returnedA return is confirmed received.
order.cancelledAn order is cancelled.
cod.collectedCash is collected at delivery.
settlement.issuedA settlement batch is confirmed.
statement.issuedA statement is generated for a postpaid account.
wallet.low_balanceYour wallet falls below its low-balance level.
invoice.issuedAn invoice is generated for your account.
payment.failedA payment attempt fails.
usage.thresholdThe shipping company's plan passes its monthly order limit.