Skip to main content
Keebai Loyalty runs the reward program behind your counter: a customer enrolls, earns points or stamps, redeems them, and carries the card in Apple Wallet or Google Wallet — where you can update the balance and push a message without an app. The API covers the operational half of that. Your point of sale accrues, your backend redeems, and your automation issues the card. Designing the program and the card stays in the Keebai portal.

The data model

Program

The rules. One of three kinds — points, stamps, or tiers — and only the matching rule set is populated.

Membership

One customer inside one program. Carries the balance, the stamp count, the tier, and the serial number the wallet card is keyed by.

Ledger entry

One movement: an accrual, a redemption, or a correction. Records the balance it resulted in.

Wallet template

The card design for a program — colours, fields, barcode. Versioned, and one version is published.
A program has many memberships and many template versions. A membership has many ledger entries and up to two passes, one per wallet platform.

The three program types

The program decides which metric a movement touches. You never pass a metric — POST .../ledger on a stamps program moves stamps, on a points program moves points.
There is no reward or redemption entity. A reward is a free-text string in rules.stamps.reward, and a redemption is a ledger entry with kind: "redeem" — nothing links it to a specific item, and nothing checks that the balance covers it. A redemption larger than the balance clamps to zero rather than failing.If you need a prize catalogue, a redemption code, or a rule that blocks an unaffordable redemption, build it on your side and use the ledger as the accounting record.

What the API does and does not do

Writes are deliberately narrow. Everything the API can write is something a system does many times a day; everything it cannot is something a person configures once.

Endpoints

Programs and templates

Memberships and ledger

Wallet passes

A full round trip

Enroll a customer, give them the card, and start accruing.
The card in the customer’s pocket updates on step 4 on its own. You do not push anything.

The customer identifier is yours

customer.id is a free-text string that you choose. It is not a Keebai contact id, not a CRM customer id, and nothing validates that it points at anything. Whatever you send at enrollment is copied into the membership and never resolved again. Rename the customer in your CRM and the membership keeps the old name until you enroll them somewhere new.
Pick something stable and reproducible — your POS customer id, your CRM primary key. It is the value you will pass to customer_id to find the membership again, and it is what makes enrollment idempotent: a second enrollment with the same customer.id in the same program returns 409 instead of creating a duplicate.
If you would rather look people up by phone, that works too: GET /v1/loyalty/memberships?phone=912345678 matches on a digit subsequence, so a stored +56 9 1234 5678 is found without you normalising anything.

Balances are a projection

The stored balance on a membership is maintained as movements happen; it is not summed from the ledger on read. Two consequences matter:
Serialise your movements per membership. Two accruals fired concurrently against the same membership can lose one of them. Queue them, or accrue once per transaction rather than once per line item.
Point expiry is a read-time view, not a movement. When a program sets rules.points.expiration_days and the window since enrolled_at has passed, GET on the membership reports balance_points: 0 — but no ledger entry is written and the stored balance is untouched. If that customer then earns points, the new balance continues from the old stored value, not from zero. The ledger’s balance_after is the honest number; the membership’s balance_points is the displayed one.

Scoping

Loyalty is company-wide. No loyalty record carries a project, so every read and write covers your whole company regardless of which project the token was minted for. Same behaviour as CRM reads, and unlike messaging, where writes stay inside the token’s project. The loyalty feature flag gates the portal, not the API. A company without loyalty enabled that calls with a scoped token gets empty lists, not a 403.

Next steps

Enroll a customer

The first write you will make.

Record a movement

Accrue, redeem, and correct.

Issue a wallet pass

Put the card on the customer’s phone.

Scopes

loyalty:read and loyalty:write.