Skip to main content
Coupons are the core reward delivery mechanism in Zupy. When a customer redeems a reward, a coupon is created with a unique code (CZ-XXXXXXXX). The partner can then list, display, and validate (use) that coupon at the point of sale.
Prerequisites: Your API key (zupy_pk_*) and familiarity with the Authentication page. If your integration requires OTP for redemption or coupon usage, see the OTP Flow guide first.

Coupon Lifecycle Overview

Every coupon follows this flow:
1

(If required) Get an OTP session

Most companies enable require_otp_for_redemption and require_otp_for_coupon_usage by default — without a valid X-OTP-Session header, the redeem and validate calls below return 403 otp-required. See OTP Flow for the full request → verify dance.
2

Browse Available Rewards

Query the reward catalog to see what the customer can redeem.
3

Redeem Reward

Create a coupon by redeeming a reward for a specific customer. Returns the coupon’s KSUID id and the customer-facing CZ- code.
4

List Coupons

View the customer’s coupons, filtered by status. Use this to find the KSUID id you’ll need for validation.
5

Validate (Use) Coupon

Atomically mark the coupon as used at the point of sale. Pass the KSUID id in the path — not the CZ- code.

Two identifiers, one coupon

Every coupon carries two identifiers: Both work as the {coupon_id} path parameter on the validate endpoint (case-insensitive on the CZ-/CP- form). When a customer hands you a receipt at the POS, you can validate directly with the printed code — no extra lookup needed. To go from CZ- code → full coupon record (for example, to check status before validating), use the dedicated lookup:
Returns the matching coupon (or an empty data array if not found) with its id, customer_id, status, valid_until, etc.

Step 1: Browse Available Rewards

Query the reward catalog to see what rewards are available for a customer. The customer_id parameter enriches each reward with the customer’s redemption status.
Key query parameters: Response fields:
Example response:

Step 2: Redeem Reward (Create Coupon)

Redeem a reward for a customer. This creates a coupon with a unique code and deducts points from the customer’s balance.
Request body: Response fields:
Example response:
If your integration’s OTP policy requires verification for redemption, include the X-OTP-Session header. See the OTP Flow guide.

Redemption Errors

Step 3: List Customer’s Coupons

Retrieve all coupons for a customer, optionally filtered by status.
Key query parameters: Response fields:
Example response:

Step 4: Validate (Use) Coupon

Validate a coupon to mark it as used. This is an atomic operation — concurrent validate calls are race-safe (uses database-level locking).
POS ergonomics: {coupon_id} accepts both identifier formats — pass whichever you have:
  • KSUID id (e.g. 16a33f27fbbc1801d63d56d2027) — returned by GET /coupons/issued/
  • coupon_code (e.g. CZ-914F15F3 or CP-LEGACY01) — printed on the customer’s receipt or wallet pass
Lookup is case-insensitive on the code path, so cz-914f15f3 resolves the same as CZ-914F15F3. The response always echoes the canonical upper-case form.Use the CZ- code when a cashier types it from a receipt; use the KSUID when you already obtained it from a previous redeem or coupons/issued/ call.
Send an empty POST body (no JSON needed). Response fields:
This is an atomic operation. The backend uses select_for_update() to prevent race conditions. If two concurrent validate calls hit the same coupon, only one will succeed — the other returns 409 Conflict.
Example response:
If your integration’s OTP policy requires verification for coupon usage, include the X-OTP-Session header. See the OTP Flow guide.

Validation Errors

POS Quick Lookup — validate by receipt code

When a customer hands you a printed coupon code at the point of sale, you have two equivalent ways to validate it. Use whichever fits your flow.
If you don’t know the customer_id ahead of time (cashier scanning a printed code from a customer they don’t recognize), the lookup-first path tells you both customer_id and status so you can decide whether to proceed and which OTP session to attach.

Multi-Use Coupons

Some rewards create coupons that can be used more than once. The remaining_usages and total_usages_allowed fields track usage:
A “3x Free Coffee” reward creates a coupon with total_usages_allowed: 3:

Coupon Expiration

Coupons have a valid_until date set at redemption time, calculated as:
  • Auto-expires: a validate call on an expired coupon returns 409 Conflict
  • Check before displaying: partners should compare valid_until with the current time before showing a coupon to the customer
  • Expired status: coupons past their valid_until date will show status: "expired" in list responses
Expired coupons cannot be reactivated. The customer must redeem the reward again to get a new coupon (if the reward allows re-redemption).

Reward & Coupon Stats

Aggregate how many coupons were issued (rewards redeemed) and how many were used — company-wide or per reward.
Query parameters (same filters as the list): A status filter is ignored here so the breakdown always spans every status.
Response

Full Example: Complete Coupon Flow

End-to-end flow validated in production 2026-05-26: request OTP → verify → browse rewards → redeem → list coupons → validate. Skip the OTP block if your integration’s policy disables require_otp_for_redemption and require_otp_for_coupon_usage.

Next Steps

Z$ Tokens

Learn how Z$ tokens work and how they can cover points gaps in reward redemption

OTP Flow

Set up customer identity verification for sensitive operations

API Reference

Browse all endpoints with request/response schemas