Tokens & sessions
Signatures and API keys authenticate your backend. Tokens scope individual requests. Cheggout issues three kinds, and using the wrong one is the most frequent integration error.
The three credentials
| Credential | Minted by | Scope | Accepted by |
|---|---|---|---|
| Reference token | getSecuredReferenceToken | Your channel | GetReward |
| User token | GetAuthToken | One user | FetchRewardInfo, GetAllScratchCardsByUserId |
| Session ID | GenerateSessionInfoV2 | One user's session | SDK launch, hosted microsite launch |
Reference token — channel-scoped
Identifies you, not a user. GetReward takes the user identity from the virtualId in the
request body instead.
That means one token serves all your users, so fetch it on a schedule and cache it — hourly, from a background job. Do not mint one per request.
Scheduled job (hourly) ──► getSecuredReferenceToken ──► cache
Every GetReward call ──► read from cache
User token — user-scoped
Carries a specific user's Cheggout customer identity. Reveal and history endpoints reject a reference token, because acting on a specific user's reward requires proving which user.
Mint one when a user session starts, cache it for that user, and refresh when it expires.
Session ID — SDK and hosted launches
Not a bearer token but a session handle. Your backend requests it with a signed call and passes it,
with the virtualId, to the SDK or a launch URL. Cheggout validates it and the user lands already
signed in.
Lifetimes
| Credential | Lifetime |
|---|---|
| Reference token | Refresh hourly from a scheduled job |
| User token | Varies by channel — commonly 1 hour |
| Session ID | Bounded; treat as valid for the user's active session only |
TBD Exact lifetimes are configured per channel and are not self-service.
Responses carry an expireDate field, but its format is not yet formally specified, so do not
parse it as a contract. Confirm your channel's lifetimes with Cheggout, and build a refresh path
that reacts to a 401 rather than one that predicts expiry.
Refresh strategy
There is no refresh endpoint. You re-mint by calling the same signed token API again.
Rules that keep this safe:
- Retry once. A second consecutive auth failure is a configuration problem, not a stale token — do not loop.
- De-duplicate concurrent refreshes. Under load, many requests will notice expiry at once. Use a single-flight lock so one refresh serves them all.
- Refresh proactively for the reference token: a scheduled job with a margin (for example every 50 minutes for an hourly token) means user-facing calls never pay the refresh cost.
- Never retry a business failure as if it were auth.
"rewards": []orREWARD NOT FOUNDmean the platform answered successfully — re-minting a token changes nothing.
Using tokens
Bearer tokens go in the Authorization header:
Authorization: Bearer <token>
The Bearer prefix is required, with exactly one space. Sending a bare token is rejected.
Anyone holding a token can act within its scope until it expires. Keep them out of URLs, out of logs, out of analytics, and off the client except where the surface requires it (the widget and SDK legitimately hold a user token). Never put a reference token on a client — it is channel-wide.
Choosing the right token
| I want to… | Use |
|---|---|
| Create a scratch card after a payment | Reference token |
| Reveal what's behind a card | User token |
| Show a user's reward history | User token |
| Launch the SDK or a microsite | Session ID |
| Read the deals catalogue | Neither — API key |
If a reveal returns an authentication failure while card creation works, you are almost certainly sending the reference token where a user token is required.