How members earn XP
XP accrual is rules, not code. Your organization defines XP rules - data, managed from the console - and the accrual engine evaluates them against every incoming event:- a rule matches an event type (e.g. an
engagement.*orcommerce.*event) plus optional conditions on its fields; - its award is a fixed amount or a formula over event fields, optionally with a per-event cap;
- when several rules match one event, the awards are summed and the most restrictive cap applies;
- rules are versioned with effective time windows - a rule change never rewrites history, it supersedes from a point in time.
What can stop an award
A matching rule is necessary for an award, not sufficient. Before XP is issued, the accrual engine runs your organization’s earning controls, and any one of them can decline to pay. A declined award is a normal, recorded outcome, not an error: the event was accepted and stored, nothing was written to the ledger, and sending the same event again gets the same answer. Don’t retry it. The same-content guard is always on, including for organizations that have configured nothing. One content item pays a member once for a given action, for life. Reading an article and answering the quiz on that same article stay separately earnable, because the guard keys on the action as well as the item. It reverses nothing - XP already issued stays issued, and only future repeat awards stop. Where a canonical event carries no stable content identity (a purchase, for example) there is nothing to key on, and the guard doesn’t apply. Earning events lists which event types the guard covers and which stay repeatable. Four further controls are optional and configured per organization from the console. Each one, and the guard, records the reason it declined:
Daily windows run midnight to midnight in the organization’s own earning
timezone, which isn’t necessarily UTC.
A sponsor campaign award runs the three controls that measure what the member
has already been paid:
content_already_earned, action_lifetime_limit and
action_daily_limit. It skips the two XP-counted ones (member_daily_cap,
org_daily_pool), which measure what the organization gives away out of its own
budget rather than out of a sponsor’s. An admin grant runs none of the five.
None of this shows up in the /v1/track or /v1/ingest response, which reports
only whether the event was accepted; accrual happens after. To show a member
what they can still earn, read GET /v1/users/{user_id}/xp-earning. It returns
their remaining XP for the day, the per-action headroom, the instant the day
resets, and recent refusals already written as member-facing sentences. An
organization with no earning controls answers limited: false, and there’s
nothing to show.
Where XP comes from
Every award is a transfer from somewhere - that’s the double-entry part. Organizations fund awards from their engagement reserve, or from a sponsor campaign: a budgeted pool a sponsor funds, drawn down as its rules award XP, reported against its budget. Admins can also make direct grants to a member, with a recorded reason.Tiers
Tiers rank members by qualifying XP - what they’ve earned, separate from what they’ve spent. Your organization defines ordered thresholds (Bronze/Silver/Gold-style) with attached benefits, computed on lifetime qualifying XP by default or on a rolling 365-day window if configured. A member’s spendable balance going down (redemption) doesn’t demote them.Redemption
Members spend XP on rewards from your organization’s catalog. A redemption burns the spent XP - ledger-first, then settled on-chain - so total supply always reconciles. Eligibility is checked when the claim is made, and a claim already made is never taken back. If a member’s tier later falls, the rule applies only to new claims. A reward with no minimum tier behaves as it always has: every member can claim it when the existing cap, stock, and XP balance checks allow it. A claim can be refused for one of these reasons. Treat these as expected outcomes, not generic server errors:
Keep a catalog useful while you gate it: putting most rewards behind the top tier makes the program read as closed. This is merchandising guidance, not a product restriction.
A tier that rewards are gated at cannot be deleted while they still require it. Deleting one, or saving a ladder that leaves one out, is refused with
TIER_HAS_GATED_REWARDS (409) and nothing is written. The blocking rewards come back in error.details.rewards, each with its id, name, and the minTierId it requires - so when a single save removes several tiers, you can tell which removal each blocker is about. Clear or move minTierId on those rewards, then delete the tier.