Automation templates.
Ready-made Make, n8n, and Zapier workflows that send an in-person sale to the loyalty program once, safely, and only when your till marks it as staff-completed.
The Connect any POS recipe explains the shape of a till connection. These templates are that recipe already built: a workflow you import, fill in, and turn on, for the platform you already use.
Each one does the same job. A sale closes on the till, the workflow checks the sale is one it should forward, and it calls the purchase endpoint once with a stable reference so retries cannot award twice.
What they forward, and what they drop
Every template is built for in-person sales that a staff member completed, and nothing else. That boundary is deliberate. Loyalty earned at the counter is what they are built for, and a sale they cannot place at a counter is one they refuse.
A workflow drops a sale, without calling the loyalty program at all, when:
- the request does not prove it came from your till, in whichever way that platform checks (see How the workflow knows the sale is yours)
- the sale is not marked as completed in person by a staff member: an online checkout, an invoice, a payment link, or a self-service order
- it is not a new completed sale: a refund, a cancellation, or an edit
- the sale has no member on it, which is what a guest sale looks like
- the sale is missing its sale id, amount, currency, or completion time
None of the templates sends a points value. Points come from your card rules, so a workflow can never invent a different earning rate. Refunds are not reversed automatically either. Correct those at the counter the way you would a mistaken sale.
The workflow trusts your till on the in-person question. It cannot inspect your POS; it reads a field the sale carries and refuses anything not marked staff-completed. Sending that field truthfully is your side of the bargain, and it is what keeps online orders out of a counter-only program.
What your till has to send
Whichever platform you choose, the workflow expects one JSON object per completed sale:
| Field | What it carries |
|---|---|
in_person_staff_completed |
true only when a staff member completed this sale in person. Anything else is dropped |
event_kind |
sale_completed. Refunds, cancellations, and edits carry something else and are dropped |
sale_id |
The till's own id for the sale: its transaction, receipt, or order number. The workflow appends it to the Workflow ID from your setup page, and the two together are the retry-safe reference, so a retry of one sale must carry the same value. Keep it to letters, digits, ., _, and - |
member_identifier |
The customer's member UUID, unique identifier, member number, or email. Absent on a guest sale, which is dropped |
amount |
The sale amount as a decimal string, for example 24.50 |
currency |
The three-letter code, matching the loyalty card's currency exactly |
completed_at |
When the sale completed, as 2026-08-26T09:41:07+02:00 or 2026-08-26T07:41:07Z |
Most tills need a small bridge to produce this: a webhook template on the POS, or a few lines in whatever already listens for closed sales. That bridge is the one piece these templates do not provide.
Set up one workflow per till, and pick that till's number on the setup page so each workflow gets its own Workflow ID. Two tills that both number their sales from 1 would otherwise send the same reference for two different sales, and the second would come back as a replay of the first.
How the workflow knows the sale is yours
A webhook URL is not a secret you can rely on, so each template proves the caller is your till before it does anything. How it does that differs per platform, and sending the proof the wrong way fails before the workflow even runs:
| Platform | What your till sends |
|---|---|
| Make | A pos_source_secret field in the JSON body, carrying the shared secret you set. It is the filter's first condition |
| n8n | An X-POS-Source-Secret header, checked by the webhook's Header Auth credential before the workflow runs. There is no secret field in the body |
| Zapier, Catch Hook trigger | A pos_source_secret field in the JSON body, matched by the filter's first condition |
| Zapier, POS-app trigger | Nothing extra. The app's own connection authenticates it, and the recipe drops that condition |
Pick a long random value that only you and the bridge know, and rotate it if it is ever exposed.
Choosing a platform
| Make | n8n | Zapier | |
|---|---|---|---|
| What you get | An importable blueprint | An importable workflow | A step-by-step recipe you build |
| Plan needed | Free or higher | Self-hosted, no paid plan | Paid, to publish |
| Where the sale data rests | Make's run history | Your own server | Zapier's task history |
| Run on the platform | Live, end to end | Live, end to end | Followed in the editor, end to end |
n8n is the choice when the data matters. Self-hosted n8n keeps the member identifiers, amounts, and run history on infrastructure you control. It also holds your shared secret as a credential rather than as a field in the payload, so the secret stays out of the run log. Make and Zapier are hosted services: the sale passes through them and stays in their run history under their own retention settings, and on a webhook trigger that history includes the shared secret in plain text. Rotate that secret if a run log is ever exposed.
n8n also checks more before sending. It validates the amount, currency, time format, and sale-id shape locally and answers your till with a reason when something is wrong. Make checks that the fields exist and forwards the values as they are, so a malformed sale id or amount is refused later, by the loyalty program, as a 422.
Zapier's gate is publishing, not building. The recipe needs a multi-step Zap with Webhooks by Zapier and Filter, which are paid features. You can build and test the whole thing on the free plan; turning it on needs the paid one.
What leaves your installation
Whichever platform you pick receives, for every sale it forwards: your base URL, the Integration API key, the card id, the member identifier, the amount, the currency, the sale reference, the completion time, the note, and the answer the loyalty program sends back. On a webhook trigger it also receives everything else your till puts in the request. For Make and a Zapier Catch Hook that includes the shared secret itself, since it travels as a body field; n8n takes it as a header credential instead, so it stays out of the run data, and a Zapier POS-app trigger sends no shared secret at all because its own connection authenticates it. A hosted platform keeps those values in its run history under its own settings.
Prefer a member UUID or unique identifier over an email address where the till can give you one. It identifies the member just as well and travels through fewer hands.
Where to get the files
Sign in to your business account and open Integrations → Automation templates. Every artifact this page describes is there to download, along with the plan each platform needs and the setup steps for it: the Make blueprint, the n8n workflow, and the Zapier recipe. Nothing is fetched from Make, n8n, or Zapier when you open it: the files ship with your installation, so a vendor outage cannot slow the page down, and looking at a template is not something a vendor gets told about.
If you cannot see that page in your navigation, the entry is hidden for either of two reasons, and the navigation does not tell you which:
- Your operator has not enabled the Agent API on this installation. That is an installation-wide switch, not something your business can turn on.
- Your plan does not include the Agent API.
The two are only distinguishable by asking for the page directly. Every page in your business account sits under a language segment, so use the one you are signed in with: /en-us/partner/integrations/automation-templates, or /nl-nl/…, /de-de/… and so on. A 404 means the feature is off for the whole installation; a 403 means the feature exists but your plan does not include it. Leave the language segment out and you get a 404 whatever your plan is, which tells you nothing. Either way the fix goes through your operator; the answer just tells them which lever to pull.
The same two conditions gate Integration API keys, which is the other thing you need. If you can create an Integration API key, you can download these templates.
Setting one up
The three differ in detail, and the first four steps are the same:
- Download and import the template from Integrations → Automation templates (Make and n8n), or download the recipe and follow it step by step (Zapier).
- Copy in the values the setup page gives you. The Before you start tab holds each one, ready to copy: your installation address, the card that earns, the currency it uses, and a Workflow ID built from the platform you picked, the card you picked, and which till this workflow serves. Nothing here asks you to invent a naming scheme, and nothing in the Workflow ID identifies a sale: your till's own transaction, receipt, or order ID is added to the end by the workflow. Paste them where the template asks; every template ships with obvious stand-ins (
REPLACE-WITH-YOUR-WORKFLOW-IDand the like) and no credential of anyone else's. Your Integration API key is the one value the page cannot hand you: it is shown once, when you create it. - Set up the Integration API key. In n8n it is a credential you create on your side after import, and an imported workflow never carries a working one. In Make it is a header value you paste into the imported blueprint. In Zapier you enter it as you build the step.
- Turn it on. In Make the blueprint already carries Immediately as data arrives, so an import arrives with the right scheduling: that text is a label, not a switch, and you only need to read it. The control next to it turns the scenario itself on, and Make confirms with a Save and activate dialog.
The last step depends on how your workflow is triggered, and the two are not interchangeable.
If the workflow listens on a webhook, which covers every Make and n8n setup and Zapier's Catch Hook:
- Create the webhook. An imported artifact never carries one, so the import prompts you for a fresh webhook and you create it yourself. In Make, open the Webhooks module, add one, and copy its address. That is what stops an imported template from pointing at somebody else's hook.
- Set the shared secret in the place that platform expects it (see How the workflow knows the sale is yours). In n8n it is the webhook's Header Auth credential; in Make and a Zapier Catch Hook it is a body field.
- Point the till at that webhook URL, sending the fields above with every sale, including the shared secret.
If the Zap is triggered by your POS app instead:
- Connect the POS app in Zapier and pick its sale-completed trigger. The connection is what authenticates the trigger, so there is no shared secret to set and no webhook URL to point anything at. The recipe drops that filter condition entirely.
- Map the trigger's fields to the request, as the recipe's steps describe. The in-person staff-completed fact is still required; only the proof-of-origin changes.
The n8n workflow carries its own setup note inside the canvas, and the Zapier recipe is written as the steps themselves. The Make blueprint has no in-canvas note; this page is its instructions.
Proving it works
Run these three in order, on a real card with a test member:
| Step | Send | Expect |
|---|---|---|
| 1 | A test sale | 201, points awarded |
| 2 | The identical sale again, same reference | 200, replayed: true, the same transaction, no new points |
| 3 | The identical sale under a replacement Integration API key | 200, replayed: true, the same transaction |
Step 2 is the one worth watching. It is the difference between a till that double-credits on a timeout and one that does not.
Then send a sale the workflow should drop, such as an online order or one whose source proof is missing or wrong, and confirm the loyalty program was never called.
When something comes back wrong
| Answer | What it means | What to do |
|---|---|---|
401 |
The Integration API key is missing, wrong, or revoked | Check the key in the workflow. A revoked key needs a new one |
403 |
The key lacks write:transactions, or the business does not have the Agent API |
Fix the key's scopes, or ask your operator about the entitlement |
404 |
The card, member, or staff member was not found for this business | Check the card id, and that the member exists and is active |
409 PURCHASE_REFERENCE_CONFLICT |
This reference is already recorded with different sale facts | Stop. Do not retry. The sending workflow is reusing a reference across sales |
409 PURCHASE_REFERENCE_IN_FLIGHT |
The same sale is still being processed | Retry the identical request shortly |
410 |
The original purchase was corrected or erased | The sale stays settled. Do not retry |
422 |
The request is malformed, the currency does not match the card, the card is inactive, or the completion time is outside the window a new sale may use, meaning more than 7 days old or more than 5 minutes in the future | Correct the request. A till whose clock runs fast fails every sale this way, so check the clock before the payload |
429 |
Too many requests | Back off, honor Retry-After |
503 PURCHASE_RETRYABLE_FAILURE |
A temporary failure | Retry the identical request with backoff. The reference keeps it safe from double-crediting |
503 PURCHASE_CLAIM_KEY_UNAVAILABLE |
The app key that protects this sale record is not configured | Retrying will not clear this. Your operator restores the previous APP_KEY from backup; see Keep your APP key recoverable |
Full field rules and every response are in Record a purchase.
Verification and maintenance
All three were run on 28 August 2026, each in the shape it ships in today.
The n8n workflow was imported into a clean instance and driven with nine fixtures: accepted sales forwarded with a stable reference through an identical retry, every rejected case refused without a loyalty call, and a workflow left unconfigured refusing to send anything at all.
The Make blueprint was imported into a real Make account and switched on. An accepted sale was recorded, the identical sale sent again returned the original transaction instead of awarding twice, and a request with the wrong shared secret and one without the staff-completed fact both reached the loyalty program not at all. A last accepted sale confirmed the scenario was still running, which is what makes those two zeros mean refusal.
The Zapier recipe was followed in the real editor. The accepted sale passed the filter and reached the loyalty program, the same sale sent again came back as a replay with no second award, and a sale without the staff-completed fact was stopped at the filter. Five of its filter conditions only check that a field is present at all, for the member identifier, sale id, amount, currency, and completion time; those five have never been exercised one by one. It is a recipe rather than a published template, because publishing needs the paid plan described above.
Make, n8n, and Zapier are other companies' products and change on their own schedule. A template that stops importing cleanly after a platform update is a template to report, not a defect in your loyalty program.
Related topics
- Connect any POS: The recipe these templates implement, and the REST alternative
- Record a purchase: Field rules, retry-safe behavior, and every response
- POS integration: The full cashier workflow, member lookup, rewards, and vouchers
- Production checklist: Key handling, retries, and app-key recovery before you go live
- Enabling & Managing Keys: Create an Integration API key with the Point of Sale preset