API

Callbacks: receipt confirmed and shipment closed

3 min read 27 Sep 2026 Cargoflow Team

Summary

A callback is a request the warehouse makes to the customer's system when something important happens, so it does not have to keep asking. Today there are two: receipt confirmed and shipment closed. Each one is signed with a secret that only the warehouse and the customer know.

How it is set up

There is no screen: we set it up. We need the web address that will receive the callbacks (it has to accept POST requests with JSON), which events they want and whether it is for the whole warehouse or just one customer. We hand over the secret for checking the signature.

  • Scoped to one customer: receives only that customer's events. This is the normal case for a customer using the API.
  • Whole warehouse: receives the events of every customer in the warehouse. Only makes sense for the warehouse's own system.

The shape of each callback

They all arrive with the same envelope and three headers:

POST https://erp.example.com/warehouse-callbacks
Content-Type: application/json
X-WMS-Event: receipt.confirmed
X-WMS-Signature: sha256=5d41402abc4b2a76b9719d911017c592...

{
  "event": "receipt.confirmed",
  "occurred_at": "2026-10-07T11:58:03-05:00",
  "data": { ... }
}

Receipt confirmed

Sent when someone confirms one of the customer's receipts. See Entry and review.

"data": {
  "receipt_id": 958,
  "receipt_number": "REC-NT-00058",
  "external_ref": null,
  "status": "confirmed",
  "lines": [
    { "product_id": 3301, "expected_qty": 120, "received_qty": 118, "damaged_qty": 2 },
    { "product_id": 3302, "expected_qty": 40, "received_qty": 40, "damaged_qty": 0 }
  ]
}

Shipment closed

Sent when someone closes one of the customer's shipments (the truck left). See Closing a shipment.

"data": {
  "shipment_id": 1204,
  "shipment_number": "SH-NT-00077",
  "reference": "SO-5512",
  "external_ref": "ERP-2026-5512",
  "status": "shipped",
  "carrier_name": "Southern Logistics",
  "tracking_number": "SL-889201",
  "lines": [
    { "product_id": 3301, "ordered_qty": 60, "picked_qty": 60 },
    { "product_id": 3302, "ordered_qty": 30, "picked_qty": 18 }
  ]
}

picked_qty is what was actually loaded. In the example, the second line went out short: 18 of 30.

Checking the signature

The X-WMS-Signature header carries sha256= followed by the HMAC-SHA256 of the exact request body, computed with the secret. The customer's system must compute the same over the body as it arrived (before parsing it as JSON) and compare. If it does not match, it must discard the callback.

expected = "sha256=" + hex(hmac_sha256(secret, raw_body))
accept only if expected == X-WMS-Signature header

Timing, retries and failures

  • The callback goes out after the receipt or shipment has been saved, through a background queue. Confirming or closing never waits for the customer's system.
  • The warehouse waits up to 3 seconds to connect and 5 for the answer.
  • It makes two attempts in a row. If both fail, the callback is lost: it is not tried again later.
  • An error answer from the customer's system (for example, a 500) counts as delivered for the warehouse: there is no failure log to review.
Important: these callbacks are best effort. If the customer's process must hear about it no matter what (for example, to invoice their own customer), it should also poll the status now and then with the shipment look-up or the receipt look-up.

Current limits

  • There are no callbacks for a notice created, a shipment allocated, stock adjustments or cancellations.
  • Lines carry the internal product id, not the SKU. Match them by position with what was sent.
  • On receipts created from a notice sent through the API, external_ref comes empty (see Sending an advance ship notice through the API). Match by receipt_id.
  • There is no screen with the history of callbacks sent.

Good practices

  • Answer fast (in under 5 seconds) and process afterwards: if the customer's system is slow, the warehouse treats the callback as failed.
  • Tolerate repeats: with two attempts, in rare cases the same callback can arrive twice. Use receipt_id or shipment_id to avoid processing it twice.
  • Always check the signature.

Frequently asked questions

Do receipts entered by hand also trigger a callback?

Yes. The callback goes out for any confirmed receipt or closed shipment of the customer, whether it came in through the API, the client portal, EDI or was entered at the warehouse.

Can a lost callback be resent?

There is no button for that. The customer's system can recover the data by looking up the receipt or the shipment through the API.

Need help?

If something did not work as expected, our support team can help you.

Was this article helpful?

Share: