← SimpleApps Loyalty

Loyalty API

← SimpleApps Loyalty

Loyalty API — developer reference

1. Get an API key

In your dashboard go to Settings → Integrations and create a key. Pick the scopes it needs (see below). The full key is shown once — copy it and store it securely on your server. Keys can be revoked at any time.

Keys are secrets that issue value — never expose one in a browser, mobile app, or public repository. Keep it server-side only.

2. Authentication

Send your key as a Bearer token on every request:

Authorization: Bearer lk_xxxxxxxxxxxxxxxxxxxxxxxx

All endpoints accept and return application/json over POST. Base URL:

https://prod.simpleapps.co.za/webhook

3. Identifying a customer

Customers are identified by their mobile number in international format (e.g. +27821234567). This is the same number they use on their loyalty card.

4. Idempotency (important)

Every call that issues value — accrue and redeem — requires an external_ref: a unique id from your system (typically your transaction/receipt id). If the same external_ref is sent twice for a business, the second call is a safe no-op that returns idempotent_replay: true instead of stamping or redeeming again. This makes retries safe.

5. Scopes

ScopeAllows
accrueAdd stamps from a purchase
readLook up a member’s balance
redeemRedeem a reward
enrollEnrol a new member

6. Endpoints

POST/loyalty/api/v1/accruescope: accrue

Add one or more stamps to a member’s card after a purchase.

curl -X POST https://prod.simpleapps.co.za/webhook/loyalty/api/v1/accrue \
  -H "Authorization: Bearer lk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mobile": "+27821234567",
    "qty": 1,
    "external_ref": "receipt-10432"
  }'

Response

{
  "ok": true,
  "error": null,
  "member_found": true,
  "stamped": true,
  "idempotent_replay": false,
  "membership": { "progress": { "stamp_count": 3, "target": 10 }, "reward_available": false },
  "member": { "mobile": "+27821234567", "first_name": "Nomsa" }
}

Fields

  • mobile (required) — the customer’s number.
  • qty (optional, default 1) — stamps to add (e.g. 2 items = 2).
  • external_ref (required) — your unique transaction id.
  • card_id (optional, default c1) — for businesses running more than one punch card.

If the member has not joined yet, accrue returns error: "member_not_found". Enrol them first (with consent) via /enroll.

POST/loyalty/api/v1/member/lookupscope: read

Check a member’s current balance and whether a reward is waiting — ideal for a prompt at the till.

curl -X POST https://prod.simpleapps.co.za/webhook/loyalty/api/v1/member/lookup \
  -H "Authorization: Bearer lk_..." \
  -H "Content-Type: application/json" \
  -d '{ "mobile": "+27821234567" }'
{
  "ok": true,
  "member_found": true,
  "membership": { "progress": { "stamp_count": 9, "target": 10 }, "reward_available": false },
  "member": { "mobile": "+27821234567", "first_name": "Nomsa", "surname": "Dlamini" }
}
POST/loyalty/api/v1/redeemscope: redeem

Redeem a reward the member has earned (e.g. hand over their free coffee at the till).

curl -X POST https://prod.simpleapps.co.za/webhook/loyalty/api/v1/redeem \
  -H "Authorization: Bearer lk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mobile": "+27821234567",
    "reward": "punch",
    "external_ref": "receipt-10433"
  }'
{
  "ok": true,
  "was_eligible": true,
  "redeemed": true,
  "idempotent_replay": false,
  "membership": { "progress": { "stamp_count": 0, "target": 10, "rewards_pending": 0 } }
}
  • reward (optional, default punch) — the reward type to redeem: punch, game, birthday or referral.
  • external_ref (required) — your unique transaction id.

If nothing is redeemable, returns error: "no_reward_available".

POST/loyalty/api/v1/enrollscope: enroll

Enrol a customer as a member. Consent is required — you must have the customer’s agreement to receive loyalty messages (POPIA).

curl -X POST https://prod.simpleapps.co.za/webhook/loyalty/api/v1/enroll \
  -H "Authorization: Bearer lk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mobile": "+27821234567",
    "first_name": "Nomsa",
    "consent": true
  }'
{
  "ok": true,
  "enrolled": true,
  "newly_created": true,
  "member": { "mobile": "+27821234567", "first_name": "Nomsa" }
}

Without consent: true, returns error: "consent_required" and does not enrol.

7. Errors

All responses are HTTP 200 with an ok boolean and an error code when ok is false:

errorMeaning
invalid_api_keyKey missing, wrong, or revoked.
insufficient_scopeThe key lacks the required scope.
missing_external_refexternal_ref was not supplied.
member_not_foundNo member with that mobile at your business.
no_reward_availableNothing to redeem for that member.
consent_requiredEnrolment attempted without consent.