Reconciles the wallet balance against the loyalty partner's counter for
campaigns where the partner is the source of truth (Carrefour LOYALTY).
Computes the delta between the partner counter and the local ledger
balance, writes a sync ledger entry for the difference (if any), and
returns the updated balance. For campaigns without a partner counter
configured, returns the current ledger balance unchanged.
Requires the partner's X-User-Token for LOYALTY campaigns. The
response shape matches GET /me/loyalty/ledger/balance so callers can
treat it as a balance refresh.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Why this endpoint exists
On some campaigns the platform is not the source of truth for a customer's loyalty balance — the
retailer's own system is. Points are earned and spent across that retailer's whole estate, and the
platform sees only the slice that flows through it.
That means the local ledger can be behind. A customer who earned points elsewhere this morning
will not see them here until something reconciles the two.
This endpoint is that reconciliation point: it reads the authoritative counter, computes the delta
against the local ledger, writes a sync entry for the difference, and returns the updated
balance.
On campaigns where the platform owns the balance it is a no-op that returns the current ledger
balance unchanged — so it is safe to call unconditionally rather than branching on campaign type.
How the authoritative counter is read is agreed per integration. What the platform callsand what credential it presents depend on what your system exposes, so treat this endpoint's
connection to your side as something we scope together rather than a fixed contract. Bring us
the system you have.
See Loyalty wallet integration for how this fits with the write side, and
for why awarding tokens directly on a partner-counter campaign needs care.
When to call it
Before showing a balance the customer will act on:
- On the storefront's loyalty or account screen
- Before building a basket where the balance drives the discount
- After the customer says "my points are wrong"
Not on every page load. Where the counter lives outside the platform this makes a synchronous call to that system, so it is slower than a plain balance read and depends on that system being up.
Request
curl -X POST https://api.acc.funtrips.io/v2/me/loyalty/ledger/sync \
-H "x-campaign-id: $CAMPAIGN_ID" \
-H "Authorization: Bearer $END_USER_TOKEN"No request body.
Credentials for the connected system
Where the authoritative counter lives outside the platform, reading it may require a credential
that identifies the customer to that system, supplied on the request. Whether one is needed, and
what it looks like, is settled when your integration is scoped.
If your integration uses a short-lived credential, fetch it immediately before this call.Credentials of that kind are often valid for minutes. One acquired at page load and used when the
customer clicks is frequently expired already — which surfaces as a401that looks like a
broken session but is not. See the error table below for how to tell them apart.
Response
200 OK — the same shape as GET /me/loyalty/ledger/balance, so you can treat this as a balance refresh and share one render path.
{
"data": {
"external_user_id": "customer-4815162342",
"tokens": 1780,
"last_updated_at": "2026-09-03T14:22:51Z"
},
"meta": { "correlation_id": "…" }
}As with the plain balance read, tokens carries no unit — read token_unit from campaign config.
What actually happens
- The authoritative counter is read.
- The delta against the local booked balance is computed.
- If non-zero, a
syncledger entry is written for the difference — visible in ledger entries. - The updated balance is returned.
The delta can be negative: a customer who spent points elsewhere gets a sync entry that reduces
their balance. Sync means "agree with the system of record", not "add points".
Errors
| Status | type | Meaning | What to do |
|---|---|---|---|
401 | — | Funtrips token missing or invalid | Re-authenticate |
401 | wallet-sync-auth-required | The connected system needs a credential that was not supplied | Supply it and retry |
502 | wallet-sync-provider-failed | The connected system returned no balance | Retryable; fall back to the local balance |
500 | wallet-sync-campaign-unreachable | Campaign metadata unavailable | Retryable |
500 | — | Unexpected fault | Retryable |
Distinguishing the two 401s
401sThey mean different things and need different handling. wallet-sync-auth-required means the
connected system's credential is missing or stale — the Funtrips session is fine. A bare 401
with no slug means the Funtrips session is the problem.
Branch on type. Treating the first as "logged out" throws away a perfectly good session and sends
the customer back through login for nothing.
Degrade, do not block
A 502 means the partner is down. The customer's local balance is still readable and still usable — the discount will be computed from the ledger the platform holds.
Fall back to the plain balance read and carry on. Blocking a purchase because a loyalty partner is unreachable costs a sale to fix a display discrepancy.
