Returns a paginated history of loyalty token transactions for the authenticated user.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Why this endpoint exists
The balance is a number; this is the story behind it. Every award, spend and reversal, newest first — what a customer needs to answer "where did my points go?" without contacting support.
It is also the audit trail. Each entry names the flow that created it, so a discrepancy can be traced to a specific voucher, checkout or partner sync.
Request
curl "https://api.acc.funtrips.io/v2/me/loyalty/ledger/entries?limit=20" \
-H "x-campaign-id: $CAMPAIGN_ID" \
-H "Authorization: Bearer $END_USER_TOKEN"| Parameter | Type | Notes |
|---|---|---|
limit | integer | 1–100, default 20 |
next_token | string | Cursor from meta.pagination.next_token |
Response
{
"data": [
{
"entry_id": "voucher:WELCOME2026",
"campaign_id": "3c1e5a90-7b2d-4f61-9a83-1d4e7f2b8c05",
"external_user_id": "customer-4815162342",
"type": "award",
"tokens": 500,
"status": "BOOKED",
"balance_after": null,
"reason": "Welcome voucher",
"reason_code": "VOUCHER_REDEEMED",
"source": "vouchers",
"meta": null,
"report_date": "2026-09-01T10:00:00Z",
"creation_date": "2026-09-01T10:00:03Z",
"processed_date": "2026-09-01T10:00:04Z"
},
{
"entry_id": "checkout-discount:7c1b3f9a-2d4e-4a6f-9b1a-3c5e7d2f8a0b",
"type": "spend",
"tokens": 1100,
"status": "BOOKED",
"…": "…"
}
],
"meta": {
"correlation_id": "…",
"item_count": 20,
"pagination": { "limit": 20, "next_token": "eyJwayI6…" }
}
}Reading an entry
| Field | Meaning |
|---|---|
entry_id | Deterministic id, prefixed by the originating flow. Opaque string, not a UUID |
type | award (tokens in) or spend (tokens out) |
tokens | Always positive. type carries the direction |
status | PENDING, BOOKED or FAILED |
balance_after | Reserved — always null today |
reason / reason_code | Human-readable and machine-readable explanation |
source | The subsystem that wrote it |
report_date | When the movement is considered to have happened. May predate creation_date |
creation_date | When it was written to the ledger |
processed_date | When it reached a final status. null while PENDING |
tokens is unsigned — read type
tokens is unsigned — read typeA spend of 1100 reduces the balance by 1100. There are no negative token values.
const signed = entry.type === 'spend' ? -entry.tokens : entry.tokens;Handle these two types. A refund of previously spent tokens arrives as an award — see
Refunds look like awards below for how to label it distinctly.
Only BOOKED entries have moved the balance
BOOKED entries have moved the balancePENDING has not been applied yet. FAILED never will be. If you sum entries to explain a balance, filter to BOOKED — otherwise your total will not match the balance endpoint, and the customer will believe one of the two screens is lying.
balance_after is always null
balance_after is always null
Do not build a running-balance column frombalance_after.The field is reserved for future use and is not populated.
To show a running balance, walk
BOOKEDentries backwards from the current balance — and read
the ordering note below before you do.
report_date can predate creation_date
report_date can predate creation_dateAn integrator may supply the date a movement actually happened, which can be earlier than when the platform recorded it — a backfilled in-store purchase, for instance.
Display report_date ("earned on 1 September"), because that is what the customer recognises. Be aware that a list sorted newest-first by ledger order is not necessarily sorted by report_date, so a naive running-balance walk can produce a sequence that looks wrong to a customer reading down the page.
entry_id tells you where it came from
entry_id tells you where it came fromThe prefix names the flow that produced the entry:
entry_id | Origin |
|---|---|
voucher:<code> | A voucher redemption awarded tokens |
checkout-discount:<checkout_id> | Loyalty spent on a checkout |
loyalty_sync:<…> | A partner balance sync corrected a drift |
<original_entry_id>:reversal | A refund of a checkout discount |
A bare ksuid, e.g. 2gK8xQ1mZp4vY7nB3tL6wZ9hJ | Written by the retailer through POST /admin/loyalty/ledgers/{id}/entries |
Retries of the same upstream event collapse onto the same id, which is what makes the ledger idempotent. That also makes entry_id the right thing to quote in a support conversation — it points at a specific cause.
Refunds look like awards
When a checkout is reversed, the tokens burned on it are credited back as an award entry — id
<original_entry_id>:reversal, reason_code: CHECKOUT_DISCOUNT_REVERSAL,
source: checkout_discount_reversal. To label refunds distinctly in a history view, match on the
reason code or the id suffix rather than on type.
Errors
| Status | Meaning |
|---|---|
401 | Token missing, expired or invalid |
500 | Unexpected fault. Retryable |
A customer with no history gets 200 with an empty array. Unlike the balance endpoint, there is no 404 for a customer who has never transacted.
Notes
external_user_idis PII-adjacent. Do not log entries wholesale; logentry_idand the correlation id.- Formatting needs the unit. As with the balance,
tokenshas no unit — readtoken_unitfrom campaign config.
