Lewati ke konten utama

Redeem a voucher from a POS

Your cashier scans or types a voucher code. Your POS checks it with XQUARE and, if it is good, redeems it. That takes two calls: validate, then redeem.

Scopes: vouchers:read for validate, vouchers:redeem for redeem.

1. Validate (optional, no side effects)​

POST /v1/vouchers/validate
Authorization: Bearer xq_live_…
Idempotency-Key: pos-17-check-000482
Content-Type: application/json

{ "code": "XQ-7KD2-9PLM" }

Like every POST, validate needs an Idempotency-Key; use a new one for each check. You can also send location_id (the location the cashier is at) and branch_id (see Name the branch below).

The answer depends on the code:

  • A voucher of your Space that can be redeemed now: 200 with "valid": true, the voucher and its template.
  • A voucher of your Space that is refused: 200 with "valid": false, an error object (code and message) and the voucher. For example, the voucher has expired or is used up.
  • A code that does not exist, or belongs to another Space: 404 not_found.
{
"valid": false,
"error": { "code": "voucher_expired", "message": "Voucher has expired" },
"voucher": { "id": "…", "code": "XQ-7KD2-9PLM", "status": "active", "redemptions_used": 0, "redemptions_max": 1, "…": "…" }
}

Show your cashier a message for each code. Never parse the English message; it can change.

2. Redeem​

POST /v1/vouchers/redeem
Authorization: Bearer xq_live_…
Idempotency-Key: pos-17-receipt-000482
Content-Type: application/json

{ "code": "XQ-7KD2-9PLM" }

A successful redemption answers 201. The body has the redemption (id, location_id, redeemed_by) and the voucher with its new status and counts.

A refused redemption answers 422 with the reason as the error code, for example voucher_expired or max_redemptions_reached.

Redeem runs the same checks as validate. You can skip step 1 if you do not need to show the voucher before charging.

Always send an Idempotency-Key. Use one key per redemption attempt, for example your receipt number. If the network drops and you retry with the same key within 24 hours, you get the original redemption back with Idempotent-Replayed: true. You do not get a second redemption, and you do not get max_redemptions_reached either. The same key with a different code or location returns 409 idempotency_key_reused. See Retries and idempotency.

Name the branch​

If your Space limits vouchers per branch, validate and redeem must say which branch the voucher is used at. Send branch_id. A location_id that identifies exactly one of your branches also works. The redemption counts against that branch's allocation.

  • The branch must be one of your Space's branches. If it is not, you get branch_not_in_space.
  • Without a branch you get outlet_required or branch_required.

Codes your POS should handle​

codeTell the cashier
voucher_expiredExpired
not_yet_validNot valid yet
max_redemptions_reachedAlready used
voucher_voidCancelled by the merchant
invalid_day · blockout_dateNot valid today
wrong_locationNot valid at this location
booking_scoped_voucherMust be used with its booking
crossbrand_redeem_deniedBelongs to another brand
staff_confirm_requiredThis perk must be confirmed by staff at the counter
outlet_required · branch_requiredPick the branch
outlet_not_allocated · outlet_quota_exhaustedNot available at this branch

If the allocation cannot be checked right now, you get 503 temporarily_unavailable. Retry with the same Idempotency-Key.

Full list: Errors.

Listing vouchers and templates​

GET /v1/vouchers and GET /v1/voucher-templates list your Space's vouchers and voucher templates, newest first. Page with limit and offset; the answer has data and has_more.

Getting told instead of asking​

Subscribe to voucher.redeemed to keep your back office in step when a voucher is redeemed anywhere: in the vendor panel, at another outlet, or by another POS. See Webhook events.