Zapier recipes
Zapier's Catch Hook is one URL, which is the opposite of how the other two webhook settings are arranged: those are split by subject, because an n8n flow is built per subject. So the first thing to do here is give Zapier the address that receives everything.
Setting it up
- In Zapier, create a Zap with the Webhooks by Zapier → Catch Hook trigger and copy its URL.
- In StorePilot: Settings → Notifications → All events (one address), paste it, save.
- Leave the two subject addresses empty. They win for their own subjects when set, so filling one in would quietly take those events away from Zapier.
- Make sure Payload contract is Version 2. Sites created before September 2026 are on version 1, whose events have five different shapes — a single Zap cannot branch on them cleanly, which is the reason version 2 exists.
Send a test from the same panel. The drill arrives in the site's own contract, so what you build the Zap against is what the real events will look like.
Branching on the event
Every version 2 payload has the same shape, so one Filter or Paths step keyed on event
splits the whole stream:
event → what to do
lead.created → create a CRM contact
lead.converted → mark that contact as a customer, stop any nurture sequence
error.created → post to a Slack channel if data.error.severity is "critical"
uptime.down → page whoever is on call
revenue.silence → tell the shop owner nothing has sold in the window
The fields a Zap usually needs are in the same place for every event:
| Field | Meaning |
|---|---|
event | Which of the twelve events this is |
site.name | Which shop, by name — no id→shop table to maintain |
occurredAt | When it happened (sentAt is when we posted) |
links.dashboard | A link straight to the thing, for the Slack message or the ticket |
id | An idempotency key, for de-duplicating a replayed delivery |
Cancelling an abandoned-cart flow
This is the recipe worth writing down, because until version 2 it could not be built.
A cart arrives as lead.created with data.lead.leadKind = "checkout". If you mail that cart an
hour later, you are racing the customer to their own receipt: they may have paid in the
meantime, and nothing told you.
lead.converted is what tells you. It fires when the checkout is paid, carries the same
data.lead.id, and adds data.order with the external order id, the total and paidAt.
- Path A —
eventislead.createdanddata.lead.leadKindischeckout: start a Zapier Delay For of one hour, then send the recovery mail. - Path B —
eventislead.converted: look the customer up bydata.lead.emailand set a field your Path A mail checks, or remove them from the sequence in your ESP.
Path B needs somewhere to write, because Zapier cannot reach into a delay that is already
running. A single-row lookup table (Zapier Tables, Airtable, a Google Sheet) keyed on
data.lead.id is enough: Path B writes converted = true, and Path A reads it after the delay
and stops if it is set.
:::note A declined card is not a conversion
lead.converted fires only on paid. A checkout that was created and then failed at the
gateway stays a live cart — and that shopper is exactly the person the recovery mail is for.
:::
Verifying the signature
Zapier's Catch Hook does not verify signatures for you. If you set a signing secret, add a Code by Zapier step as the first action and reject anything that fails — otherwise the URL alone is the whole credential, and it travels in every Zap history entry.
See the signature section for the exact material: on version 2
it is <timestamp>.<raw body>, and the timestamp is in X-StorePilot-Timestamp.
What this cannot do
There are no retries. Delivery is fire-and-forget; if Zapier is unreachable, the event is
gone. For anything you cannot afford to miss, point webhook_url at a queue you control and let
Zapier read from that.