Appearance
Product authentication flow
A shopper points the camera at the code on a label, the app proves the product is genuine, and the points and collectible behind that code are claimed. This is product authentication — unrelated to signing a user in, which is the other thing "authentication" means in this app.
Play the flow
Three paths: a signed-in scan, a scan by someone who has not logged in yet — who gets sent to the login screen before anything can be claimed — and the branch where the reward is held until a receipt is checked.
4:4713
point the camera at the code on the label
tap an NFC tag instead
enter the code by hand
Screen not designed yet — drawn from what the documented flow requires.
Space plays and pauses, ← and → step. These screens have no design yet, so they are drawn from what the documented calls require — the step list and the endpoints are what to build against.
Verifying and claiming are different things
The shape that surprises people is that a verified scan is not a claimed scan. verify/product/:authCode proves the code is real and returns the product; nothing has been granted at that point. Claiming is a separate call against the same authCode, and points and collectibles are two separate calls again:
| Step | Endpoint | What it does |
|---|---|---|
| Verify | verify/product/:authCode | Proves the code, logs the view, returns the product plus brl_id / pvl_id |
| Post-verify | post_verify/product/:brl_id | The side effects of a successful scan. Customer-only |
| Claim points | redeem-point/product | Grants the points on that code |
| Claim collectible | redeem-collectible/product | Grants the collectible, if the code carries one |
So a product can carry points, a collectible, both or neither, and the UI has to handle a code that verifies but has nothing left to give.
Three ways a code reaches the app
The journey above starts with an authCode in hand. There are three ways to get one, and only the first is a camera:
- A scanned or typed code — straight into
verify/product/:authCode. - An NFC tap — same endpoint, with
nfc_identifierin the payload (lowercased server-side).event_countrides along for devices that count puffs. - A photo of the label —
verify/authcode-by-ocrtakesmultipart/form-datawith a singlefilefield, max 10 MB, runs OCR and resolves the code. It is the one call in this flow that is not JSON.
There is also verify/authcode/:authCode — a lightweight validity check that writes no view log. Use it when you want to know whether a code is real without recording a scan against it.
Scanning without an account: verify yes, claim no
The verify call has a guest mount and a customer mount on one handler. The difference is attribution: /user/mobile/verify/... records the scan against the caller, /brand/mobile/verify/... does not.
So anyone holding the product can prove it is genuine. Claiming is where the app stops them. There is no inline claim screen: tapping claim while signed out sends the user into login or sign up, and the claim runs afterwards with a session behind it. post_verify is customer-only too, so a signed-out scan skips it entirely.
A field that looks like a guest claim, but is not one
redeem-point/product accepts an optional customer object — email or phoneNumber, a name, even a password — and the endpoint reference describes it as the standalone claim path. The app does not use it. It forces a login instead, which is consistent with the endpoint being marked Customer. Whatever that object serves, it is not this flow — do not build a guest claim screen on the strength of the field existing.
The hold / release branch
When a reward depends on something being checked — a receipt, a manual verification — the scan does not grant it outright:
hold/points-collectiblereserves the points and collectible against theauthCode.- The receipt goes through its own review.
release/points-collectibletakes the sameauthCodeplus an optionalreceiptIdand turns the held reward into a granted one.
Nothing in the mobile API set says what happens to a hold that is never released — no expiry endpoint, no list-my-holds call. That is worth settling before the screen is built.
A scan is also a loyalty event
The last thing a successful scan does is move campaigns. Sending campaigns/almost-completed with event_item.event_type: "scan-auth" returns the campaigns this scan just pushed closer to completing — which is the hand-off into the rewards flow.