Skip to content

Webhooks

Instead of asking the API again and again, your system can be told. When something happens to a request or a deal your company is part of, Quatlas sends a signed POST to your address. The body is thin on purpose: it names the event and the records, and your system reads the details through the API with its own scopes.

  1. Your company’s Owner or an Admin opens Settings > Integrations, goes to Webhooks and chooses Add webhook (with two-step verification).

  2. They pick the integration, enter the address and choose the events (see Events). An integration holds at most 5 subscriptions.

  3. The signing secret (whsec_...) is shown once. Put it into your receiver straight away; it is never stored and cannot be shown again. If it is lost, rotate it.

  4. Press Send test to send a webhook.test event and check your receiver.

A subscription has the integration’s mode: a live integration’s subscriptions receive live events only, a test integration’s receive its sandbox’s events only ("livemode": false).

  • https on port 443, with a fully qualified host name.
  • No IP address, no user name or password, no # fragment.
  • The host must resolve to public addresses only. It is checked when it is saved and again at every delivery; private, loopback, link-local, carrier-grade NAT and cloud metadata addresses are refused.
  • Redirects are never followed.
POST /quatlas/webhooks HTTP/1.1
Host: erp.example.com
Content-Type: application/json; charset=utf-8
User-Agent: Quatlas-Webhooks/1
Quatlas-Event-Id: 0193a7c2-6f1e-7c4b-9a51-3e2f1d0c9b8a
Quatlas-Event-Type: shipment.milestone
Quatlas-Timestamp: 1791963721
Quatlas-Signature: v1=5f0c...e91a
{
"id": "0193a7c2-6f1e-7c4b-9a51-3e2f1d0c9b8a",
"type": "shipment.milestone",
"occurredAt": "2026-10-14T07:40:00Z",
"livemode": true,
"data": {
"shipmentRequestId": "<request-id>",
"externalReference": "EBA-2026-000123",
"dealId": "<deal-id>",
"milestoneType": "Departed",
"milestoneAt": "2026-10-14T07:40:00Z"
}
}
Header Value
Quatlas-Event-Id The event’s id (a UUID), the same as id in the body.
Quatlas-Event-Type The event’s type, the same as type in the body.
Quatlas-Timestamp When this delivery was signed, in Unix seconds.
Quatlas-Signature v1= and the lowercase hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with the signing secret. During a secret rotation, one v1= value per valid secret, comma-separated.

Verify every delivery before you trust it: see Verifying signatures.

Answer with any 2xx status within 10 seconds. Do the work after you answer (put the event on a queue), not before. Anything else counts as a failure: another status, a redirect, a timeout, a connection or TLS error, or an address that is refused.

A failed delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours: seven attempts over about 21 hours. After the last one, the delivery is marked Failed. Each attempt is signed again with a fresh timestamp. While your company’s account is suspended or without the Integration API, deliveries wait and no attempt is counted.

Deliveries to one subscription go one at a time, but a retried delivery can arrive after a later event. Do not rely on order: use occurredAt, and read the current state through the API when it matters.

A delivery can arrive more than once (a retry after an answer that was lost, a redelivery). The same event always carries the same Quatlas-Event-Id. Store the ids you have processed and skip a repeat.

After 20 failed attempts in a row, the subscription is switched off. Your company’s Owner and Admins are told (in the app, by e-mail and on their phones), and so is the Quatlas team. Fix the receiver, then choose Enable: the address is checked again and the failure count starts from zero. Deliveries still waiting within their 30 days are sent once it is enabled.

Every delivery is in the delivery log under Settings > Integrations, kept for 365 days and downloadable as CSV (each attempt: time, event, result, duration and the first 256 bytes of your answer). The body of a delivery is kept for 30 days; within that time Redeliver sends it again, with the same event id, starting the retry schedule from the beginning. After 30 days the body is gone and the delivery cannot be sent again.

Send test queues a webhook.test event to the subscription, whatever events it names, with "livemode": false. It is tried once, not retried, and it does not count towards auto-disable, so you can test a disabled subscription before you enable it again. A subscription can have at most 3 test events waiting at once.

Rotate secret issues a new secret, shown once. You choose how long the previous secret keeps signing: 24 hours (the default), 7 days, or not at all. During that time every delivery carries two signatures, the new one first, so your receiver accepts either while you deploy the new secret.