Sync loyalty balance with the loyalty partner

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.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

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 calls

and 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 a 401 that 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

  1. The authoritative counter is read.
  2. The delta against the local booked balance is computed.
  3. If non-zero, a sync ledger entry is written for the difference — visible in ledger entries.
  4. 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

StatustypeMeaningWhat to do
401Funtrips token missing or invalidRe-authenticate
401wallet-sync-auth-requiredThe connected system needs a credential that was not suppliedSupply it and retry
502wallet-sync-provider-failedThe connected system returned no balanceRetryable; fall back to the local balance
500wallet-sync-campaign-unreachableCampaign metadata unavailableRetryable
500Unexpected faultRetryable

Distinguishing the two 401s

They 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.

Headers
uuid
required

Campaign scope for this request. Filters content to the specified campaign.

string

Partner-issued user session token. Required when the active
campaign's loyalty partner uses it for balance reconciliation
(e.g. Carrefour). Token TTL is partner-defined (typically
≤5 minutes); requests received after expiry return 401.

string
enum
Defaults to application/json

Generated from available response content types

Allowed:
Responses

Language
Credentials
Bearer
JWT
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json
application/problem+json