The platform is hosted for you. There is nothing to install, nothing to update and no server to run: everything happens in a browser, on any device, and your customers use their phone without downloading an app.
Five people use the platform in five different ways. Read your row and skip the rest.
| You are | You use | Read |
|---|---|---|
| Setting up a new programme | The admin console | Section 2, then 3 |
| A customer or member | The branded app on your phone | Section 4 |
| Store or restaurant staff | The till, or a counter tablet | Section 5 |
| Onboarding businesses onto the platform Platform team | The platform and operations consoles | Section 8 |
| Running the platform itself Platform team | Configuration held by the provider | Section 9 |
The three addresses you will use, where yourbusiness is the tenant id you were given:
| Screen | Address |
|---|---|
| Admin console (you) | /admin?tenant=yourbusiness |
| Member app (your customers) | /app?tenant=yourbusiness |
| Printable sign-up poster | /poster?tenant=yourbusiness |
If you are a customer running a loyalty programme, sections 1 to 7 are yours. Sections 8 and 9 are for the team that operates the platform, and you can stop reading at the end of section 7.
Nothing in section 2 needs a developer, a release or a support ticket. Every setting is configuration, applied live, and it reaches your customers' phones within seconds.
Do these eight steps in order, top to bottom, exactly as the console presents them. At the end you will have a live, branded loyalty programme with a launch promotion and a printed sign-up code.
/admin?tenant=yourbusiness.Treat the admin key like a password. Anyone with it can change your earn rate and see your member list. If it is ever shared too widely, ask your platform operator to issue a new one.
The top panel decides the economics of the whole programme. Four numbers:
| Setting | What it does | Sensible starting point |
|---|---|---|
| Points per £1 | How fast points build. Multiply by the point value to see the discount rate you are really offering. | 3 for retail, 10 for hospitality |
| Redeem threshold | How many points before a customer can turn them into a voucher. | 500 for retail, 250 for hospitality |
| Welcome voucher | What a new member gets the moment they join. The strongest single lever on sign-up rate. | £5, or £3 for lower basket values |
| Frequent visitor reward | Every N separate visit days, the customer earns a voucher automatically. Rewards frequency rather than spend. Set visits to 0 to switch it off. | Every 5 visits, £5 |
Press Apply rules. The new rate applies from the next sale. Existing balances are never altered by a rule change.
A visit is a distinct calendar day with a purchase, so three sales in one afternoon count once. That is deliberate: it stops the reward being farmed at the counter.
any applies to the whole basket. A named category such as coffee or sides applies only to matching items.Edit or retire from the same table. Retiring stops the boost immediately, including for customers who had activated it. Points already earned are never taken back.
A promotion multiplier and a standing category bonus never stack. The customer gets the better of the two. This is deliberate, so a generous week cannot quietly become a triple giveaway.
Good first promotion: "Double points on everything this week", category any, multiplier 2, not requiring activation.
Your colours, name and symbol are already set. This changes the layout and feel of the member app:
| Look | Best for |
|---|---|
| Wallet card and stamps | Cafes, takeaways and independents. Warm, with a membership card and a visible stamp card. |
| Clean and premium | Fashion, beauty and considered retail. Large type and plenty of whitespace. |
| Bold and playful | Quick service and high energy brands. Colour blocks and chunky buttons. |
Press Apply look, then Preview member app to see it exactly as a customer will. You can change it whenever you like; members see the new look the next time their app refreshes.
Download QR gives you the code on its own as an image, for a menu, a leaflet or a receipt footer.
The address under the code is filled in for you and is the same one your customers already use. Glance at it before a big print run, and if it is ever not the address you expect, tell your platform provider rather than printing.
Signup asks for a name and email only, on purpose: every extra field costs you sign-ups. Anything else is asked later, in the app, where the customer has time and a reason to answer. This panel controls that.
| Field | Why you might want it |
|---|---|
| Mobile number | Text messages, and identifying a customer at the till by phone number. |
| Birthday (day and month) | Birthday rewards. Never asks for a birth year, which most customers dislike giving. |
| Postcode | Catchment and store planning. |
| Home store | Store level reporting, and relevant offers for multi site businesses. |
| Date of birth | Only needed if you sell age restricted goods and want the platform to enforce a minimum age. |
Each field has three settings: Not asked, In the app, or At signup. "At signup" makes it required on the join form and will cost you some completions, so use it sparingly.
Press Save.
Chips are the interests a customer can pick in the app, in your own words: vegan, menswear, spicy, decaf. They decide which offers are marked "For you" and who gets notified about a new promotion.
The panel tells you what is connected. There are two kinds of message, and they behave differently on purpose:
| Kind | Examples | Consent |
|---|---|---|
| Transactional | Login codes, welcome message, e-receipts, reward earned, account deletion confirmation | Always sent, because the customer just asked for them |
| Marketing | Campaigns and promotional messages | Only to customers who ticked the opt-in when they joined |
| Status | Meaning |
|---|---|
| delivered | Your provider accepted it. Check your inbox. |
| recorded only | No email provider is connected yet, so it was logged rather than sent. Fine for a pilot, not for going live. |
| not opted in | A marketing message to someone who has not agreed to marketing. Working as intended. |
| failed | Your provider rejected it, and the reason is shown in the provider's own words. Usually an unverified sending domain. |
If no provider is connected, login codes are shown on screen instead so nobody is locked out. That is a pilot behaviour, and it is switched off in a locked down deployment (section 7.5).
Skip this step if you are running a pilot with the browser till, and come back to it when you are ready. Everything else in the programme works without it.
Connecting your till is what turns the programme from something staff have to remember into something that happens by itself. Once it is connected, every sale earns points on its own, refunds take them back, and nobody has to key anything twice.
Everything in this step is on the Tills tab of your console. There are two ways in, and which one you use depends on your till rather than on us.
You will need four things from Square, and it takes about ten minutes once.
developer.squareup.com and sign in with the same Square account your till uses.Your access token and signature key are stored for your programme alone and are never shown again, not even to you. If either is ever exposed, replace it in Square and connect again. Nobody at our end needs to see them, and nobody should ask you for them.
Points are earned when the customer is attached to the sale. In Square that is the Add customer button they may already use. If they forget, the sale goes through normally and simply earns nothing.
The panel counts both, so you can see whether the habit is sticking:
| What it says | What to do about it |
|---|---|
| Sales that earned points | Nothing. This is the number you want going up. |
| Sales with no member attached | If this is most of your sales, staff are not asking. It is a two minute conversation, not a fault. |
| Refunds handled | Points taken back automatically. A partial refund leaves the points for the part the customer kept. |
| Sales we could not accept | Rare. Usually a sale in another currency, or an amount outside the platform's limits. The reason is shown underneath. |
These counts reset when the platform restarts, so they show recent activity rather than a lifetime total. Programme figures and the change history are the permanent record.
Shopify orders can earn points on the same balance as your shop, so a customer who buys online on Tuesday and in the shop on Saturday has one balance rather than two. On the Tills tab, in the Shopify panel, copy the address shown and add two webhooks in Shopify under Settings, Notifications, Webhooks: one for Order payment and one for Refund create. Shopify shows you a signing secret once. Paste that and your shop address into the panel.
There is nothing to install and no app to approve, because Shopify's order message already contains everything needed. Points go to whoever placed the order, matched on their email address, so a customer earns online only if they used the address they joined with. Guests earn nothing, which the panel counts so you can see how many are missing out.
Online orders cannot spend points yet. That is deliberate: letting a checkout spend a balance because somebody typed an email address would let anyone spend somebody else's points. Refunds are handled: points come back in proportion to the money, as they do in the shop.
Sales from your website appear as a branch called ONLINE in the By branch table, so you can compare online and each shop side by side.
The platform integrates through one open contract rather than one till, so any till that can call a web address can use it. Your supplier does not need our permission and does not need to talk to us. Hand them three things, all on the Tills tab under What your supplier needs: the address they call, your programme id, and the link to the integration guide.
Then choose one of two ways for your tills to identify themselves. The difference is who holds the credential.
| Choose this | When |
|---|---|
| A credential for each till | You have more than one till, and each one sends its own sales. Every till gets its own credential, so you can switch one off without stopping the others, and every sale records which shop and which lane it came from. |
| Or one credential for everything | One back office system sends all your sales, or your supplier has already built against it. Simpler, but replacing that credential stops every till at once, and sales only say which shop they came from if the sender says so. |
You do not set tills up one by one, and you do not need a list of them beforehand. You get one file and send the same file to all of them. Each till sets itself up the first time it runs, and your list of shops fills in by itself as they come online.
That file cannot ring a sale. It can only set a till up, it stops working after two weeks, and you can stop it sooner. That is why it is safe to send to two hundred machines, and why the credential that can ring a sale never leaves the till it belongs to. If you lose the file, press Set up tills again: you get a new one, the old one stops working, and tills already set up carry on regardless.
Your till supplier needs to do one thing for this: call /v1/enrol once on each machine, sending the shop and lane number the till already knows. It is written up for them in the integration guide, with a working example.
Find it on the Tills tab under its shop, and press Switch off. That lane stops immediately and every other till carries on trading. To bring a machine back, set it up again with a new file. A till that is wiped and rebuilt sets itself up again on its own, and the credential it had before stops working shortly afterwards.
This is the reason to give each till its own credential. With one credential shared across an estate, switching off a lost machine means changing the credential on every till you own, on the same day, which in practice means nobody ever does it.
Once your tills are set up, every sale records which shop and which lane it came from, and a By branch table appears on the Today tab: sales, tills, takings and points for each shop. A shop with one till never sees that table, because one row saying "the shop" tells nobody anything.
A sale that arrives without a shop on it is not counted in that table, which is why a figure there can be lower than your total. That is deliberate: we would rather show you a gap than invent a branch.
/app?tenant=yourbusiness.If you want a fuller picture before you have real customers, the Demo tools panel can seed three sample members with purchase history, a return and an activated offer.
Reset tenant data wipes every member, point and receipt for your programme, and cannot be undone. Use it only before you go live.
Computed from your own ledger and receipts, not estimated. Active members, lapsed members, people who joined but never bought, average visits, average basket, points earned and redeemed, redemption rate, points liability, best selling category, and your busiest and quietest trading days.
The two most useful numbers when you are deciding what to do next: joined but never bought, which tells you whether your welcome reward is working, and quietest trading day, which is usually where a promotion earns the most.
Optional, and off until you switch it on. If your platform provider has enabled it, the Assistant panel lets you:
Everything it produces is a draft. Nothing reaches a customer until you press Save promotion. It is never given member names, email addresses, phone numbers or card numbers, only the aggregate figures above, and it cannot change points, prices or rules.
Earn rules has one setting, Remind before a deadline (days), and it covers two different things:
Members are told once about each, never twice, and get one message however many things are closing at the same time. Three notifications in a minute is how a shop loses permission to notify at all.
Set it to 0 to send none. That is a legitimate choice: a programme nobody mutes is worth more than one that interrupts. The in-app card is included in that, so 0 really does mean quiet.
Notifications only reach members who allowed them in their browser, which is never everybody. The card on the Home tab does not need permission, which is why both exist.
Two panels near the bottom of the console exist for the awkward questions rather than the day to day.
Replacing the key logs out every other browser and device using the console, including any staff member or agency you gave it to. Tell them first, and be ready to hand out the new one.
A member deleting their own account is recorded here too, as evidence the request was honoured, without keeping any of the details they asked you to delete.
Worth raising with whoever looks after your tills, because it costs them very little and it is the moment your programme is most visible.
Every sale sends back what membership was worth on it, as a single figure, and your till can put it on the screen the customer is looking at: "Your membership saved you £4.50 today". It adds up member prices, any reward used and anything paid for with points. It deliberately leaves out a discount you were giving everybody that day, so the number is honestly about being a member rather than flattered by your own promotion.
A till can also ask what a basket would earn before the customer has paid, so a customer facing screen can show the points building up as items are scanned. It records nothing, so it can be asked on every item.
Both are already in the contract your supplier builds against, described in the integration guide under "What to show the customer". Neither needs anything from us and neither costs you anything.
The member table shows every account with its balance. The cards at the top show outstanding points, their cash value, open vouchers and total ledger entries, all derived live from the transaction record, so the liability figure your finance team sees is always the true one rather than a stored total that could drift.
| Change | When it applies |
|---|---|
| Earn rules | The next sale. Existing balances are untouched. |
| New or edited promotion | Immediately, in every app. |
| Retiring a promotion | Immediately, including for customers who activated it. |
| App look | Next time each member's app refreshes, within seconds. |
| Preference chips | Immediately for new pickers. Existing choices are kept. |
| Profile fields and birthday reward | Immediately. Members are asked for new fields the next time they open the app. |
There is no app to download. A customer scans your code, joins in under a minute, and adds the page to their home screen, where it behaves like any other app: full screen, its own icon, and it works offline enough to show their card.
Six tabs along the bottom, in the order a customer needs them. The illustrations below use the wallet look with a green brand. Your own colours, name, symbol and chosen look replace these, but the layout and the six tabs are the same for every business.
These frames are the member app itself: the real screens, with the real styling, captured from a running programme. To see them on your own branding, open the member app link in section 1 on a phone, or press Preview member app in your console.
| Screen | What they can do |
|---|---|
| Home | See their balance and what it is worth in pounds, see how far off the next reward they are, redeem points for a voucher, jump straight to their card, follow their visit stamp card, and answer the occasional prompt such as adding a birthday. |
| Card | Show the rotating barcode to be scanned, read out the card number if there is no scanner, and add the card to Apple Wallet or Google Wallet. |
| Offers | See what is running, activate an activate-to-earn offer, and see which offers match their interests. |
| Rewards | See vouchers they hold with codes and values, and what they have already used. |
| History | Read every points movement explained in plain language, open an itemised receipt for any identified purchase, search receipts by item or amount, see this month's spend by category, and download everything as a spreadsheet. |
| More | Add details such as birthday, mobile or home store, pick interests, see money back so far, turn on offer alerts, read the programme's promises, download or delete their data, revoke a lost phone, and switch member. |
Tap "Already a member? Log in with email", enter the email you joined with, and you will receive a six digit code. Enter it within 10 minutes. There is no password to remember, and each code works exactly once.
If no code arrives, check you used the email you joined with and that you are on the right business's app. Each business's programme is separate, so an email registered with one is unknown to another.
Every reward states how long it lasts. Inside a fortnight it counts down in days rather than giving a date, and on the last day it says so.
When one is close, the Home tab shows an amber card above everything else: Use it before you lose it, what it is, and how long is left, with a button straight to it. It appears whether or not you ever allowed notifications, which matters because the people most likely to lose a reward are the ones who do not open the app.
A reward with no end date never produces that card. Being told to hurry about something that never expires is how people learn to ignore an app.
The History tab keeps an itemised receipt for every identified purchase including the points working, an activity list that explains every movement in plain language, a monthly spend breakdown, and a search box. The More tab shows what the programme has actually given you back in pounds since you joined.
How loyalty appears at your till depends on how your business is connected. The customer experience is always the same: identify, then the sale earns points automatically.
| Situation | What you do |
|---|---|
| Customer shows their app barcode | Scan it as you would any barcode. The screen greets them by name with their balance and any vouchers. |
| No scanner, or the code will not scan | Key in the card number shown underneath the barcode in their app. |
| Customer wants to use a voucher or pay with points | Offer it during payment. The customer chooses how many points to use, and the receipt shows every deduction. |
| A refund | Process it as normal against the original sale. The right points are taken back automatically, and anything the customer paid with is returned to them. |
| "It says my code expired" | The barcode refreshes every 30 seconds. Ask for the current one. This is fraud protection, not a fault. |
| The internet is down | Keep selling. Sales queue and points apply once the connection returns. Nothing is lost and nothing is counted twice. |
| Symptom | Likely cause and what to do |
|---|---|
| The console asks for an admin key and will not accept it | The key belongs to a different business, or it has been reissued. Check with your platform operator. Each business has its own key. |
| The console shows no figures at all | Either the key has not been entered, or the business is suspended. The key bar at the top says which. |
| "I earned fewer points than the basket total suggests" | Points are earned on the amount actually paid, after vouchers and points were used, and some items such as gift cards never earn. The receipt in the app shows the exact working. |
| No login code arrives | Wrong email, or the right email on a different business's app. Codes also expire after 10 minutes, so request a fresh one. |
| Test messages say "recorded only" | No email provider is connected yet for your programme. Fine for a pilot. Ask your platform provider to connect one before you go live. |
| Test messages say "failed" | The provider's own reason is shown next to it. Almost always an unverified sending domain or a wrong API key. |
| "Too many attempts" or "rate limit" | Signup and login are limited per network to stop abuse. Wait a few minutes. See section 7 for the limits. |
| The app briefly showed the login screen, then recovered | Momentary cloud handover. The app retries by itself. |
| The QR code opens a login wall instead of the app | Rare, and it means you are looking at a test copy of the platform rather than the live one. Check the address under the code, and tell your platform provider. |
| The assistant panel says it is not available | Your platform provider has not enabled the AI service. It is optional, and everything else works without it. |
| New customers cannot join, existing ones are fine | The free pilot has reached its member or day limit. Speak to your platform operator. Nothing is lost. |
| "Account suspended" on every screen | The programme has been paused at platform level. Nothing is deleted, and everything returns exactly as it was once it is switched back on. |
| A wallet pass downloads but will not install on iPhone | Expected until Apple merchant certificates are configured. See the release notes. |
| Thing | Value |
|---|---|
| Login code | Six digits, valid 10 minutes, single use |
| Staying signed in | 30 days, extended each time the app is used |
| Card barcode | Refreshes every 30 seconds |
| Sign-ups per network address | 5 per hour |
| Login attempts per network address | 10 per 15 minutes |
| Requests per network address | 900 per minute overall |
| Promotion multiplier | Between 1 and 20 |
| Promotion title | Up to 60 characters |
| Preference chips | Up to 12, each 2 to 40 characters |
| Free pilot | 100 members or 60 days, whichever comes first |
| Assistant calls | Capped per business per month, 500 by default |
| Points expiry | None by default. Points do not expire unless a business deliberately turns expiry on. |
Internal. This is how a new customer is created on the platform, and it is done by the team that operates the service. A business running a programme never needs this section.
Two screens: /platform to create a customer, /operations to see them all.
| Field | What to enter |
|---|---|
| Tenant id | Short, lower case, 3 to 20 characters, letters, numbers and hyphens. It appears in every address, so keep it recognisable. Cannot be changed later. |
| Business name | The legal or trading name, for your own records. |
| App display name | What customers see at the top of the app, for example "Sushi Go Club". |
| Symbol | Pick one from the row, or paste an emoji. Type nothing else in this box. |
| Tagline | One short line under the name in the app. |
| App look | Wallet, clean or bold. The business can change this themselves afterwards. |
| Colours | Primary, accent and positive. Text colours are computed at runtime so they stay readable on any colour you pick, including in dark mode. |
| Earn settings | Points per £1, welcome voucher and redeem threshold. Starting values only; the business can change them. |
| Preference chips | Comma separated, in the customer's vocabulary. |
| Catalogue and offers | Optional. Paste JSON if you have it, or leave blank and let the business add promotions themselves. |
| Platform admin key | Your PLATFORM_ADMIN_KEY. Required on any real deployment. |
The result panel shows the new business's admin key, adapter secret and sign-up QR code. The keys are shown once. Copy them into your password manager and send the admin key to the business through a channel you trust, not in the same email as the console link.
New businesses start on a free pilot by default: 100 members or 60 days, whichever comes first. Their console shows a banner with progress. When the pilot ends, new sign-ups pause and existing members carry on exactly as before, so nobody's points are affected by a commercial conversation.
/operations lists every business with when they were onboarded, how many members they have, their plan and pilot state, and direct links to their app, console and poster. From here you can convert a pilot to paid, or suspend a business.
Suspending stops that business's traffic immediately and reversibly: apps, tills and their console all return a payment required response. Nothing is deleted, and reactivating restores everything exactly as it was.
Internal. The platform is delivered as a hosted service, so customers never deploy or configure it and never see any of this. It is recorded here so the team running the service has one reference, and so a buyer's technical reviewer can be shown how the service is configured.
It runs as a plain Node.js application with no build step, and PostgreSQL is the only external dependency it needs for durable storage.
| Variable | Set it to | Why |
|---|---|---|
AUTH | Leave unset | Member tokens, signed till calls and admin keys are enforced by default. AUTH=off is the only way to run without them, it is for a throwaway local experiment, and the console shows a red warning when it is in effect. |
DATABASE_URL | Your PostgreSQL connection string | Durable storage. Without it, data resets when the server restarts. |
PUBLIC_BASE_URL | The live address of the service | Set once for the whole platform, not per customer. Every printed QR code is built from it, with the tenant added automatically. Without it, a code picks up whatever address the poster happened to be opened on, which on a preview deployment is protected by a login wall. Set it on any real deployment and it is then correct for every customer, present and future. |
PLATFORM_ADMIN_KEY | A long random string | Guards tenant creation. Without it a demo default is accepted. |
DEMO_MODE | off for production | Stops login codes being shown on screen and disables the demo credentials endpoint. |
EMAIL_PROVIDER | http or smtp | How member email is delivered. Unset means messages are recorded rather than sent. |
EMAIL_API_URL, EMAIL_API_KEY, EMAIL_FROM | Your provider's values | Works with Resend, SendGrid, Postmark, Mailgun and similar. |
SMS_PROVIDER, SMS_API_URL, SMS_API_KEY, SMS_FROM | Your provider's values | Optional. Works with Twilio style JSON APIs. |
AI_PROVIDER, AI_API_URL, AI_API_KEY, AI_MODEL | Any OpenAI compatible endpoint | Optional. Turns on the merchant assistant. Unset means it does not appear at all. |
AI_MONTHLY_CALL_CAP | e.g. 500 | Per business monthly limit. Reaching it disables the assistant for that business, never the platform. |
| Apple and Google wallet keys | Certificates and issuer ids | Optional. Enables real wallet passes rather than preview passes. |
Create an account with an email provider, verify a sending domain, create an API key, then set four variables:
EMAIL_PROVIDER = http
EMAIL_API_URL = https://api.resend.com/emails
EMAIL_API_KEY = re_...
EMAIL_FROM = Rewards <rewards@yourdomain.com>
Redeploy, then use Send test in any business's console. A rejection is shown in the provider's own words, which is usually enough to diagnose it without leaving the screen.
AI_PROVIDER = http
AI_API_URL = https://api.openai.com/v1/chat/completions
AI_API_KEY = sk-...
AI_MODEL = gpt-4o-mini
AI_MONTHLY_CALL_CAP = 500
Any OpenAI compatible endpoint works, including EU hosted and self hosted models where data residency matters. Each business still has to switch the assistant on in their own console before anything appears.
AUTH=on and DEMO_MODE=off.PLATFORM_ADMIN_KEY before showing the platform console to anyone outside your team.PUBLIC_BASE_URL to the address customers will actually use.With DEMO_MODE=off the consoles cannot fetch keys for themselves, which is the point. Each business enters their admin key once per browser and the console remembers it.
The platform is till agnostic. A till connects through a small set of signed endpoints: identify a customer, send a transaction, redeem, pay with points, void, and send a batch of queued sales after an outage. Every call is signed with the business's adapter secret, and every transaction carries an idempotency key so a retry can never double count.
There is a browser till simulator at /pos?tenant=yourbusiness for testing and demonstrations, and a working connector for Toast as a reference implementation.