Skip to content

Concepts

Points and cents, pending vs spendable, tiers, expiry, holds — the vocabulary the API uses.

Updated 2026-09-22

Every field name in the API comes from a small set of ideas. Read this once and the reference will make sense.

Money and points are integers

Everything is an integer in minor units: paise for INR, cents for USD. gross_cents: 249900 is ₹2,499.00. Points are the same — a programme where 1 point is worth ₹1 stores 1 point as 100. The programme's currency_name ("VibeCoins", "Stars") is what members see; the API never rounds and never uses floats.

A member

One person in one programme, identified by email (the anchor — it never changes through the API), optionally a phone and your own external_ref. Anywhere the API takes a member id you may send our M-… id, ref:<your id> or phone:<number>. A member has a state: prospect (seen on an order, has not agreed to terms), registered, active, paused, suspended, deleted, anonymised.

The ledger and the four balances

Points live in an append-only ledger: earns, adjustments, redemptions, reversals, expiries. Nothing is ever edited; corrections are new entries. From it we keep four numbers per member:

BalanceMeaning
pending_centsEarned, not yet usable — waiting for a return window or a manual approval.
available_centsUsable now.
reserved_centsHeld by an open checkout reservation.
spendable_centsavailable − reserved: what a redemption may take right now. Use this one.

lifetime_earned_cents and lifetime_redeemed_cents are running totals.

Earning

An order.paid (or order.placed / order.fulfilled, depending on the programme's timing) runs the programme's rules — base rate, tier rate, category boosts, campaigns, caps and budgets — and writes earn entries. Whether they land pending or available is the programme's release timing (immediately, on payment, on fulfilment, after the return window). order.refunded and order.cancelled write the reversal.

Tiers

Levels a member holds by meeting a threshold (spend or orders) over a window (rolling 365 days, calendar year, lifetime). A tier can change the earn rate and unlock benefits your storefront may read. Tier is evaluated by the programme, not set through the API.

Redeeming: reservations and vouchers

Two ways to spend, both take the row lock so a balance can never go negative by a race:

  • Reservation (custom checkout): quotereserve (holds points, 15 min) → confirm with an order_ref, or release. Points are only debited on confirm.
  • Voucher (till, phone order): POST /v1/members/{id}/redemptions debits immediately and returns a single-use code; confirm it when it is used, void it to return the points; unconfirmed vouchers return their points on expiry.

Expiry and holds

Points can expire under the programme's policy (fixed days, end of period, inactivity). expiring_30d_cents on a member tells you what is about to. A hold is a reservation's grip on points; under review is an earn waiting for a person to approve it.

Events are asynchronous

POST /v1/events answers 202 and processes in the background, usually within a second. Every event has a status you can read (queuedrunningcompleted / failed) and an error that says why it failed or why it was ignored (an older aggregate_version, a customer nobody could match). Webhooks tell you when balances change.

Conventions

  • Timestamps are ISO 8601 in UTC: 2026-09-22T10:31:00.000Z.
  • Member ids are the stable public form M-…; internal identifiers never appear.
  • Lists return { data, pagination: { cursor } }. Pass ?cursor= for the next page; null means the end. Cursors are stable across inserts, so a walk never skips or repeats a row.
  • Errors are RFC 9457 application/problem+json — see Errors.
  • Responses only gain fields inside /v1 — see Versioning.
Concepts — StickyTier API