Vouchereto API · v1
Redeem codes with the API
Your customer types a voucher code in your own checkout or wallet page; your server reserves it, does its work, then captures or cancels.
The flow
two-step redemption
customer types code ──► POST /v1/redemptions 201 reserved (voucher locked, nothing charged)
│
your side succeeds ─┼─► POST /v1/redemptions/{id}/capture 200 captured → credit the customer
your side fails ─┴─► POST /v1/redemptions/{id}/cancel 200 cancelled → voucher usable again
you do nothing ──► after ttl_seconds expired → voucher usable again
Two steps keep you safe: if crediting the customer fails on your side after the voucher was reserved, you cancel and the customer loses nothing.
Implementation
top-up.ts (Node SDK)
import { Vouchereto, VoucheretoError } from './vouchereto-node';
const v = new Vouchereto({ apiKey: process.env.VOUCHERETO_API_KEY! });
export async function topUpWithVoucher(user: { id: string }, code: string, customerIp: string, topUpId: string) {
let r;
try {
r = await v.redemptions.reserve({ code, currency: 'USD', customer_ref: user.id, ttl_seconds: 300 },
{ idempotencyKey: `topup-${topUpId}-reserve`, customerIp });
} catch (e) {
if (e instanceof VoucheretoError) return { ok: false, reason: e.code }; // show a message per code
throw e;
}
try {
await wallet.credit(user.id, r.amount_minor, r.currency, { redemptionId: r.id }); // idempotent on r.id
} catch (e) {
await v.redemptions.cancel(r.id, { idempotencyKey: `topup-${topUpId}-cancel` });
throw e;
}
await v.redemptions.capture(r.id, { idempotencyKey: `topup-${topUpId}-capture` });
return { ok: true, amountMinor: r.amount_minor };
}
- Normalise nothing: send the code as typed. Dashes, spaces and lower case are handled by us.
- Send the customer's IP as
X-Customer-Ipso invalid-code limits apply to that customer, not to your server. - Choose
ttl_seconds(60–1800) to cover your own processing time. Short is better. - Capture only after your side committed. Crediting must be idempotent on the redemption id (a
redemption.capturedwebhook may arrive too).
Messages for your customer
| code | Suggested message |
|---|---|
VOUCHER_INVALID | We could not find that code. Check it and try again. |
VOUCHER_ALREADY_REDEEMED | This voucher has already been used. |
VOUCHER_EXPIRED | This voucher has expired. |
VOUCHER_RESERVED | This voucher is being used in another payment. Try again in a few minutes. |
CURRENCY_MISMATCH | This voucher is in a different currency. |
UNIVERSAL_NOT_ACCEPTED | This type of voucher is not accepted here. |
TOO_MANY_INVALID_ATTEMPTS | Too many attempts. Please try again later. |
Never echo the full code back to the page, a log or an analytics tool. The API returns only
voucher_last4; use that in receipts.Reconciliation
- Store the redemption id with your own transaction.
- Nightly, list
GET /v1/redemptions?status=capturedfor the day and compare with your records; investigate differences with therequest_ids in your logs. - Payouts arrive weekly with a CSV of every captured redemption: Settlements.