Error codes
Cheggout does not use one error format across all product lines. This page catalogues every documented failure and the shape it arrives in.
Start with API conventions for the parsing strategy.
The four response shapes
Write a parser that recognises all four before branching on the content.
| Shape | Example | Where |
|---|---|---|
| Signed-API failure | {"STATUS":"FAILURE","ERRORSTRING":"..."} | Rewards Center token APIs, Bank APIs |
| Rewards business result | {"status":false,"message":"failure","rewards":[]} | GetReward |
| Reveal business result | {"status":false,"message":"REWARD NOT FOUND"} | FetchRewardInfo, inside BDATA |
| Deals Store error | {"status":404,"errorCode":"INVALID_OFFER","message":"..."} | Deals Store |
Deals Store responses carry the status as a status field in the JSON body. Always parse the body
rather than branching only on the HTTP status line.
Not every "no" is an error
These are successful responses that happen to say no. They must not trigger retries, token refreshes or alerts.
| Response | Meaning | Handle by |
|---|---|---|
{"status": false, "rewards": []} | User not eligible for a card right now | Rendering nothing |
BETTER LUCK NEXT TIME | No reward behind this card | Showing your "no luck" state |
403 NON_ELIGIBLE | Targeting excludes this user | Hiding or disabling the offer |
409 COUPON_EXHAUSTED | Stock exhausted | Showing "sold out" |
429 MAX_LIMIT_REACHED | Per-user limit reached | Showing the limit message |
| Empty array from reward history | User has no cards | Showing an empty state |
With frequency caps and probability-based campaigns, "no" will be the common answer. Design for it first.
Signed APIs
Applies to getSecuredReferenceToken, GetAuthToken, GenerateSessionInfoV2,
GetScratchCardForBank.
ERRORSTRING | Meaning | What to do |
|---|---|---|
Signature Mismatch! | The signature could not be verified | Check you signed the Base64 BDATA string, not the decoded JSON; check CHANNELID; check padding is PKCS#1 v1.5 |
Internal Error! | Platform-side failure | Retry with backoff. Escalate if persistent. |
Check for the STATUS/ERRORSTRING shape before attempting signature verification, or your
client will throw on a legitimate error.
Common causes of Signature Mismatch!, in the order worth checking:
- Signing the decoded JSON instead of the Base64
BDATAstring. - Re-serialising the payload after computing
BDATA, so the signed bytes differ from the sent bytes. - A wrong or mistyped
CHANNELID, so Cheggout verifies with a different key. - PSS padding instead of PKCS#1 v1.5.
- Using the wrong key pair for the environment.
Rewards Center
GetReward
| Response | Meaning |
|---|---|
{"status": false, "message": "failure", "rewards": []} | Not eligible — a normal outcome |
HTTP 401 | Token missing, malformed, expired, or the wrong token type |
A 401 here most often means a user token was sent where the reference token is required.
See Tokens.
FetchRewardInfo
Failure messages arrive inside the decoded BDATA as {"status": false, "message": "..."}.
message | Meaning | Handling |
|---|---|---|
BETTER LUCK NEXT TIME | No reward allocated — inactive campaign, frequency cap, empty pool, probability miss, or exhausted budget | A normal outcome. Show the no-luck state. |
REWARD NOT FOUND | No card matches this rewardId for this user — including one already revealed | Look it up in reward history |
SCRATCHCARDID NOT FOUND | rewardId missing or not a valid number | Fix the request |
CHEGCUSTOMERID NOT FOUND | The user could not be resolved | Check the token matches the user |
VIRTUALID NOT FOUND | virtualId missing from the payload | Fix the request |
BANKNAME NOT FOUND | channelName missing from the payload | Fix the request |
REWARD NOT FOUND after a timeout is ambiguousIt may mean the card never existed, or that your earlier reveal succeeded and you lost the
response. Do not assume failure — check
GetAllScratchCardsByUserId. If the card shows
isSeen: true, the reveal happened and the reward is in that response.
GetAllScratchCardsByUserId
| Response | Meaning |
|---|---|
HTTP 401 | Token missing, malformed, expired, or the wrong type |
[] | No cards — a normal outcome |
Deals Store
The most complete error catalogue on the platform.
status | errorCode | Message | Endpoints | Handling |
|---|---|---|---|---|
400 | INVALID_REQUEST | — | All | Fix the request. Refund coins if already burned. |
401 | UNAUTHORIZED | Invalid AppKey/SecKey. | All | Fix credentials. Refund coins. |
403 | FORBIDDEN | Request blocked. IP not whitelisted. | getCatalogue | Register your egress IPs |
403 | NON_ELIGIBLE | User not eligible for this offer based on targeting rules. | fetchCouponCode | Refund coins. Hide the offer. |
404 | INVALID_OFFER | Offer not found or inactive. | fetchCouponCode | Refund coins. Refresh your catalogue cache. |
409 | COUPON_EXHAUSTED | — | fetchCouponCode | Refund coins. Show "sold out". |
429 | MAX_LIMIT_REACHED | Maximum redemption limit reached for this user. | fetchCouponCode | Refund coins. Show the limit message. |
500 | INTERNAL_ERROR | Something went wrong. | All | Retry with the same extTransactionId, then refund |
Because coins are burned before fetchCouponCode is
called, each failure leaves the user short. Retry safely or refund — there is no option that leaves
the user whole without one of the two.
Note that 403 carries two different errorCode values depending on the endpoint, and 429 is a
business limit rather than rate limiting. Branch on errorCode, not on status alone.
Handling strategy
Rules worth encoding once:
- Retry only transport failures, with the same
extTransactionId. - Never retry a business no. Re-minting a token or re-sending will not change the answer.
- Never blind-retry a reveal. Reconcile through reward history instead.
- Log unrecognised bodies with the endpoint and your
extTransactionId, and report them.
Undocumented failures
TBD Error coverage is uneven. The Rewards Center and Bank APIs publish only a few strings, and there are no distinct codes for an unknown channel, an unwhitelisted IP, a malformed payload or a rate limit.
Treat any unrecognised non-success body as a generic failure: log it, fail the operation safely, and report it via Support so it can be added here. Closing this gap is a P0 item on Open items.