# Referrals with Zapier, Make and n8n

The Zapier app's triggers, actions and searches, and importable Make and n8n templates with every webhook verified.

Connect a referral program to any other tool without writing code. Zapier has
Bolt Claw's own app; Make and n8n have ready-made templates you import. All
three use the same API and the same signed webhooks as your own code would.

## The key

Make a key under **Settings, Developers** for the tool, and give it only what
the tool uses:

| Scope | For |
|---|---|
| `conversions:write` | Recording a sale, a lead or a refund |
| `referrals:read` | Reading what an event is about: the partner, the commission, the grant |
| `referrals:write` | Creating a referrer or an invite, marking a grant fulfilled |
| `webhooks:read`, `webhooks:write` | Only for the Zapier app, which adds and removes its own webhook endpoint |

The key lives in the tool, so anybody who can edit the Zap, scenario or
workflow can read it. One key per tool means revoking one stops one thing.
Marking a grant fulfilled also needs the key's maker to hold **Adjust
commissions and pay partners**: owners and admins do by default.

## Zapier

The Bolt Claw app for Zapier connects with your key and is labelled with your
workspace's name.

**Triggers** (each takes a program):

| Trigger | When |
|---|---|
| New Partner Application | Somebody applied to promote the program. |
| Partner Approved | A partner was approved. |
| New Conversion | A signup, sale or refund was recorded, with its commission when it earned one. |
| New Commission | A commission was created, with its conversion and partner. |
| Grant Ready | A reward that is not cash is ready for you to give. |
| Payout Paid | A payout was marked paid. |
| New Referrer | One of your own users became a referrer. |

**Actions:** Record a Sale, Record a Lead, Record a Refund, Create or Update a
Referrer (it returns the referral link), Create an Invite (it returns the
invite link: Bolt Claw emails nobody, so the Zap sends it), Mark a Grant
Fulfilled.

**Searches:** Find Partner (by name), Find Referrer (by your own id), Find
Program (by name). Find Referrer and Create or Update a Referrer together are
**Find or Create Referrer**.

What to know:

- **Event ID is how a retry stays one sale.** Record a Sale, a Lead and a
  Refund each ask for an Event ID: map your own order, signup or refund id. A
  replayed step sends the same one and changes nothing. Never make one up: a
  new id is a new sale, and can pay a partner twice.
- **Amounts** go in as a normal decimal (`49.00`), turned into minor units for
  the currency, or as minor units directly. A value with more decimals than its
  currency has is refused, never rounded.
- **Each Zap with a Bolt Claw trigger holds one webhook endpoint.** A workspace
  has five by default, so a sixth such Zap cannot switch on.
- **A delivery is checked before the Zap starts.** One with a wrong signature,
  or older than 5 minutes, is stopped and shown in the Zap's history.
- **Approvals.** An AI assistant's key is asked to wait for a person before it
  marks a grant fulfilled. The Zap says a person must approve it in Bolt Claw,
  with the link, and that nothing was done. A key a person made in the
  dashboard is not asked.

### Until the app is published

The Bolt Claw app is not yet in Zapier's directory. Until it is, **Webhooks by
Zapier** does the same with the same events:

1. A Zap starting with **Webhooks by Zapier, Catch Raw Hook**: it keeps the raw
   body and the headers, which the signature check needs. (Catch Hook parses
   the body first, and a parsed body can no longer be checked.)
2. Add that Zap's address as an endpoint in **Settings, Developers, Webhooks**,
   for the one event it handles, and keep the signing secret.
3. A **Code by Zapier** step that checks the signature (below) and stops the
   Zap when it does not match. Zapier may rename the headers (an app receives
   `X-BoltClaw-Signature` as `Http-X-Boltclaw-Signature`), so map them from the
   trigger's sample.
4. **Webhooks by Zapier, Custom Request** to read what the event names, with
   the header `Authorization: Bearer bc_live_...`, then whatever the Zap is for.

## Make

Four scenario templates, each a blueprint you import:

| Template | Page |
|---|---|
| Shopify order to Bolt Claw sale | [Shopify order to sale](https://boltclaw.ai/docs/referral-template-shopify-order) |
| Grant ready to credit in your own tool | [Grant ready to credit](https://boltclaw.ai/docs/referral-template-grant-credit) |
| New partner approved to a Slack message | [Partner approved to Slack](https://boltclaw.ai/docs/referral-template-partner-slack) |
| Commission earned to a Google Sheets row | [Commission to Google Sheets](https://boltclaw.ai/docs/referral-template-commission-sheets) |

To import one: create a scenario, open the **...** menu at the bottom of the
editor, choose **Import Blueprint** and pick the file. Then make the webhook in
its first module, with **Get request headers** and **JSON pass-through** both
set to **Yes** under **Show advanced settings** (a blueprint cannot carry a
webhook, so this step is always yours), and paste the secret and the key where
each template's page says.

A webhook scenario in Make switches itself off after an error nothing handled,
so every Bolt Claw call in the templates has a handler: the sale, grant and
sheet templates retry three times, 15 minutes apart, and the Slack one skips
that one message.

## n8n

The same four templates as n8n workflows. To import one: **Import from File**
(or paste the file onto the canvas), then choose a credential in every node
that asks. **Bolt Claw API key** is a **Header Auth** credential: Name
`Authorization`, Value `Bearer bc_live_...`. Publish the workflow and use the
webhook node's production address.

Self-hosted n8n needs `NODE_FUNCTION_ALLOW_BUILTIN=crypto` for the signature
check's Code node (on the task runners, if you use them); n8n Cloud allows it
already.

## Verifying a webhook in a no-code tool

Every delivery carries `X-BoltClaw-Timestamp` and `X-BoltClaw-Signature`. A
delivery is ours when the signature equals `sha256=` followed by the hex
HMAC-SHA256, keyed with the endpoint's signing secret, of the timestamp, a
dot, and the raw body exactly as it arrived. Check three things before the
delivery does anything:

1. **The signature**, over the raw body. A body the tool parsed and wrote out
   again is not the same bytes, and will not match.
2. **The time.** Refuse a timestamp more than 5 minutes from now. The timestamp
   is inside the signed text, so moving it breaks the signature.
3. **The event, from the signed body.** Route on the body's `event`, never on
   the `X-BoltClaw-Event` header, which is not signed. Ignore an event you do
   not recognise: new ones are added without notice.

How each tool does it:

- **The Zapier app** checks all three, in constant time, before a Zap starts.
- **n8n:** the Webhook node keeps the raw body (**Raw Body** on), and a Code
  node checks it with `crypto.timingSafeEqual`. The templates answer `401` to a
  delivery that fails, so it shows in the endpoint's delivery log.
- **Make** has no constant-time comparison, so the templates compare in a
  filter. That cannot leak anything: Make answers the delivery with `Accepted`
  before the scenario runs, so the sender never sees how long the comparison
  took. Make also does not promise that **JSON pass-through** hands the body on
  byte for byte. If it ever did not, every delivery would stop at the filter:
  it fails closed, never open. Make measures the 5 minutes when the scenario
  runs, so a delivery that waited in its queue (the scenario was off) stops
  too.
- **Webhooks by Zapier:** Catch Raw Hook, then a Code by Zapier step
  (JavaScript). Map the secret, the timestamp, the raw body and the signature
  into the step, and stop the Zap when this answers false. It uses only the
  Web Crypto built into the runtime, so it needs no import:

```js
async function verifyBoltClawDelivery(secret, timestamp, rawBody, signature) {
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey(
    "raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  const mac = await crypto.subtle.sign("HMAC", key, enc.encode(timestamp + "." + rawBody));
  const expected = "sha256=" +
    [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
  // Compare every character, whatever the first difference: constant time.
  let diff = expected.length ^ signature.length;
  for (let i = 0; i < expected.length; i++) {
    diff |= expected.charCodeAt(i) ^ (signature.charCodeAt(i) || 0);
  }
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
  return fresh && diff === 0;
}
```

A tool that cannot compute an HMAC over the raw body cannot verify a delivery.
There, let a delivery do nothing on its own: every body carries ids only, so
read the object back with your key and act on what Bolt Claw says. A forged
delivery can then only make the tool look at something real. The signing
secret is shown once, when the endpoint is added; rotating it stops the old
one at once, so paste the new one straight in. After 20 failed deliveries in a
row an endpoint switches itself off, and everyone who manages webhooks is
emailed.

<!-- https://boltclaw.ai/docs/referral-zapier-make-n8n as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
