Webhooks
Add an endpoint, tick the events you want, and Sailo will POST signed JSON to
it when they happen.
Anything that accepts a webhook works — Zapier’s Catch Hook, n8n’s Webhook node, Make’s Custom webhook, Pipedream, or your own server. There is no connector to install and no app to register.
Webhooks are the primary mechanism for anything live. Polling the REST API frequently enough to feel immediate is polling that will meet the rate limit on a shop with real volume — and webhooks cost nothing against it, because they are Sailo calling you.
Setting one up
Add the endpoint
Settings → Integrations in the Sailo admin. Paste your URL and tick the events you want. Up to 5 endpoints per shop.
The URL must be https and publicly resolvable. Sailo refuses private and
loopback addresses at delivery time, so a tunnel is what you want during
development rather than localhost.
Keep the signing secret
It is shown when the endpoint is created and starts whsec_. Each endpoint has
its own, so revoking one does not affect the others.
Verify before you trust
An endpoint that accepts anything knowing its URL is an endpoint that accepts anything. See verifying signatures — it is one line with an off-the-shelf library.
Send a test
The Send test button delivers a complete payload with "test": true and
every field a real one carries, including the ones that would be null on a
shop that has never had an order.
That matters more than it sounds: Zapier builds its whole field map from the first payload it receives, and a thin sample is a map that breaks on the first real sale.
What you get
| Events | 16 — see them all |
| Payload | The same object the REST API returns — see the shapes |
| Signature | Standard Webhooks, HMAC-SHA256 |
| Delivery | At-least-once, 6 attempts over 15 hours — see the schedule |
| Timeout | 5 seconds |
| Endpoints per shop | 5 |
The three rules
Everything else on these pages is detail. These three are what separates a webhook consumer that works from one that mostly works:
1. Verify the signature. Against the raw body, exactly as received. Re-serialising parsed JSON will not match.
2. Answer fast, work later. Return a 2xx as soon as you have stored the event. Sailo waits five seconds; the work belongs off the request.
3. Deduplicate on webhook-id. Delivery is at-least-once. A POST that
succeeded on your side but timed out on ours is retried, and the id stays the
same across every retry of one event.
A minimal handler
import { Webhook } from "standardwebhooks";
const wh = new Webhook(process.env.SAILO_WEBHOOK_SECRET!);
export async function POST(request: Request) {
const body = await request.text(); // raw — rule 1
const headers = Object.fromEntries(request.headers);
let event: { id: string; type: string; data: unknown };
try {
event = wh.verify(body, headers) as typeof event;
} catch {
return new Response("bad signature", { status: 400 });
}
if (await alreadySeen(event.id)) { // rule 3
return new Response(null, { status: 204 });
}
await store(event); // rule 2
return new Response(null, { status: 204 });
}Next
- Events — all 16, and what each one means.
- Payloads — the envelope, and which object arrives.
- Verifying signatures — libraries, and the recipe by hand.
- Delivery and retries — the schedule, what counts as a failure, and when an endpoint is switched off.