Authorization: Bearer <jwt>) with
the same partner-issued token you use for the webview. The /me/* action
routes have full schemas with try-it-out in the API Reference
tab; the read-only merchant browse routes are documented under
Browsing merchants below.
Endpoints at a glance
Browsing merchants
Use these to render a brand catalogue in your own UI — e.g. a merchant listing screen — before or alongside opening the webview. Both use the same partner-issued consumer JWT as the webview, and both honour your partner scope: if your account is restricted to an allowlist of brands or categories, only in-scope merchants are returned (out-of-scope ids 404).GET /merchants
Returns an array of active merchants, ordered alphabetically by name (or by
curated order when featured=true, or nearest-first when live lat/lng
are supplied).
Optional query parameters:
Each merchant object:
GET /merchants/{id}
Returns one merchant (same fields as above) plus its active outlets.
Returns 404 not_found if the merchant doesn’t exist, is inactive, or is
outside your partner scope.
Additional field:
Image paths
logoPath, bannerPath, and voucher imagePath are storage paths, not
always absolute URLs. If a value begins with /uploads/…, prefix it with the
API origin (https://vouchersx.com/uploads/…) to load the image. If it is
already an https://… URL, use it as-is.
Redemption — the important one
POST /me/redemptions is the only complex endpoint. A redemption is
atomic — the PIN check, GPS distance check, redemption limit, and code
dispensing all happen inside a single transaction. Either everything
succeeds or no row is written.
Rate limiting
5 attempts per user per 60-second sliding window. The 6th attempt returns HTTP 429 with aretry-after header (seconds):
GPS rules
FLAGGED redemptions (the gps_unavailable case) are stored for support
review. They count toward the user’s cycle redemption limit and (if the
voucher dispenses codes) a code is still dispensed.
Per-redemption limits
Two independent caps can apply:- Per-user cycle quota (
redemptionCounton the voucher): how many times this user can redeem this voucher per cycle (ONE_TIME,MONTHLY, orBIWEEKLY). Exceeded →limit_reached(422) withdetails: { limit, used, cycleKey }. - Global cycle cap (
globalRedemptionCap): how many times all users combined can redeem the voucher in the current cycle. Useful for capped-inventory promos. Exceeded →global_cap_reached(422) withdetails: { cap, used, cycleKey }.
Voucher codes
If the voucher’scodeSource is MANUAL or AUTO, the response includes a
code field — display this to the user. If the voucher has no codes
configured, code is null.
When the manual code pool is empty, the redemption fails with codes_exhausted
(422) — the redemption row is not written. The merchant needs to upload
more codes.
Favorites-only vouchers
If the voucher is markedfavoritesOnly, the user must have favorited the
merchant first. Otherwise the redemption fails with not_a_favorite
(403). Favorite via POST /me/favorites.
Wallet flow
If the voucher’sclaimFlow is WALLET, the user must claim it first via
POST /me/claims. Otherwise redemption fails with no_claim (422).
Instant-flow vouchers skip this step.
Redemption window (wallet vouchers)
Wallet vouchers can carry an optional redemption window —applicableFrom
and/or applicableUntil timestamps on the voucher. Claiming is never
blocked (the user can add the voucher to their wallet any time), but
redemption is gated to the window:
- Before
applicableFrom→voucher_not_yet_applicable(422) withdetails: { applicableFrom }. - After
applicableUntil→voucher_no_longer_applicable(422) withdetails: { applicableUntil }.
Scope enforcement
If your partner has a restricted scope (allowlist of merchants or categories), every/me/* mutation enforces it. Out-of-scope vouchers or
merchants return 403 with voucher_out_of_scope / merchant_out_of_scope
even if the user knows the UUID. The webview catalog already filters by
scope; this server-side check is the second layer of defence.