Skip to Content
Building an integrationZapier, n8n and Make

Zapier, n8n and Make

There is no Sailo app in any of these directories, and that is the design rather than a gap.

A signed webhook plus a bearer key reaches Zapier, n8n, Make, Pipedream, Activepieces, Windmill and everything behind them — a larger set than any list of logos we could maintain, and it means nothing you build depends on Sailo shipping a connector for your tool.

What you use instead: a generic webhook trigger and a generic HTTP request action. Both exist in every one of these tools.

Receiving events

Create the trigger and copy its URL

ToolStep
ZapierWebhooks by ZapierCatch Raw Hook
n8nWebhook node, POST
MakeCustom webhook
PipedreamHTTP / Webhook trigger

In Zapier, choose Catch Raw Hook, not Catch Hook, if you intend to verify the signature. The signature covers the exact bytes sent, and Catch Hook parses the body before you see it — leaving you nothing to verify against. If you are not verifying, Catch Hook is friendlier to map from.

Register it in Sailo

Settings → Integrations → add the endpoint, paste the URL, tick the events. Up to 5 endpoints per shop, so a Zap and an n8n flow can run side by side.

Send a test

Press Send test. The payload that arrives carries "test": true and every field a real one carries, including the ones that would be null on a shop with no orders yet.

This step is not optional in a no-code tool. Zapier builds its whole field map from the first payload it receives, so mapping against a thin sample produces a Zap that works in testing and breaks on the first real sale. The test payload is complete precisely so that cannot happen.

Build the rest of the flow

Map the fields you need. Two that trip people up:

  • Money is an object. Use data.total.amount for anything a person reads — "54.98". data.total.cents is 5498, and mapping it into an email is the most common mistake in this whole category.
  • Timestamps are UTC. Convert to the shop’s timeZone — read it once from GET /shop — before putting a date in front of a human.

Verifying, in a no-code tool

If your tool can run a code step, verify. It is a dozen lines and it is the difference between an endpoint that receives Sailo’s events and one that receives anybody’s.

n8n — a Code node before anything else:

const crypto = require("crypto"); const secret = $env.SAILO_WEBHOOK_SECRET.replace(/^whsec_/, ""); const headers = $input.first().json.headers; const body = $input.first().json.body; // raw string — set the node to raw const expected = crypto .createHmac("sha256", Buffer.from(secret, "base64")) .update(`${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${body}`) .digest("base64"); if (!headers["webhook-signature"].includes(expected)) { throw new Error("bad signature"); } return [{ json: JSON.parse(body) }];

Zapier — a Code by Zapier (JavaScript) step, same recipe, reading inputData.

If your tool cannot run code, the practical mitigation is a long unguessable path on the webhook URL — which every one of these tools gives you by default — plus a check that data.shop.id is the shop you expect. That is weaker than a signature and better than nothing.

The signatures page has the full recipe and the four ways it goes wrong.

Deduplicating

Delivery is at-least-once, so the same event can arrive twice.

Zapier — the Storage by Zapier app, or a lookup in whatever spreadsheet or database the Zap already writes to. Key on data.id from the body, which is the same value as the webhook-id header.

n8n / Make — a “get row where id = …” step with a branch on whether it exists. Both tools have an early-exit node for the found case.

Skipping this shows up as duplicate rows in a sheet, duplicate emails to a customer, or duplicate charges downstream, and it will not happen in testing.

Calling the API

For anything the events do not carry — a catalogue sync, a nightly reconcile, looking up a contact.

Every tool has a generic HTTP step. Configure it:

MethodGET (or POST for the two writes)
URLhttps://api.sailo.store/api/v1 plus the path
HeaderAuthorization: Bearer sailo_sk_…
HeaderContent-Type: application/json on a POST

Put the key in the header, never in the URL. Some of these tools make a query parameter the path of least resistance. A token in a URL is written into every log the request passes through, including your automation tool’s own run history — which is often shared with a team.

Paging in a no-code tool

Every list endpoint is cursor-paged. Ask for limit=100 — the maximum — and, if you need everything, loop on has_more using your tool’s iterator: Zapier’s Looping, n8n’s Loop Over Items, Make’s Repeater.

Most flows do not need to page. If you are reacting to an event, the event already carries the whole object.

Common recipes

New paid order → row in a sheet. Trigger on order.paid. Map data.id, data.customer.email, data.total.amount, data.createdAt.

New contact → your newsletter tool. Trigger on contact.created, then filter on data.marketingConsentAt being non-empty. Everybody else is a customer who never agreed to be emailed — see syncing to a CRM.

Form submission → Sailo contact. POST /contacts with email, name, tags, and sendOptIn: true if you want Sailo to ask for consent. It is idempotent by person, so a double submission updates rather than duplicating.

Membership ended → revoke a Discord role. Trigger on subscription.endednot subscription.cancelled, which is a member who has paid through the end of the period. See granting and revoking access.

Chargeback opened → task with a deadline. Trigger on dispute.opened, map data.dueBy to the due date, and branch on data.caseType so an inquiry does not get reported as a lost sale. See chargebacks.

Last updated on