Skip to content
Documentation menu

Vouchereto API · v1

SDK & tools

Everything you need is downloadable from this site. Use the SDK for Node, or any HTTP client with the signing helper from the authentication page.

Downloads

Downloads
FileWhat it is
vouchereto-node.tsNode 18+ SDK in one TypeScript file, no dependencies: typed client, request signing, webhook verification
openapi.yamlOpenAPI 3.1 description of the whole API (also as JSON): generate clients, import into tools, validate
Postman collectionEvery endpoint, with a pre-request script that signs requests and adds idempotency keys
Sandbox environmentPostman environment for https://api.vouchereto.com; paste your test key
Local environmentPostman environment for the local stack (http://localhost:3003)

Node SDK

Copy vouchereto-node.ts into your project (we will publish it on npm as @vouchereto/node). It signs every request, adds an Idempotency-Key to POSTs when you do not pass one, and throws VoucheretoError with code, status, requestId and retryAfter.

usage
// Single-file SDK: download /docs/vouchereto-node.ts (TypeScript, Node 18+, no dependencies)
import { Vouchereto, VoucheretoError } from './vouchereto-node';

const v = new Vouchereto({ apiKey: process.env.VOUCHERETO_API_KEY! });   // baseUrl defaults to https://api.vouchereto.com/v1

const voucher = await v.sandbox.createVoucher({ amount_minor: 5000, currency: 'USD' });
const r = await v.redemptions.reserve(
  { code: voucher.code, currency: 'USD', customer_ref: 'user_8842' },
  { idempotencyKey: 'order-3141-reserve', customerIp: '203.0.113.9' },
);
await v.redemptions.capture(r.id, { idempotencyKey: 'order-3141-capture' });

try {
  await v.redemptions.reserve({ code: voucher.code, currency: 'USD', customer_ref: 'user_8842' });
} catch (e) {
  if (e instanceof VoucheretoError) console.log(e.code, e.requestId);    // VOUCHER_ALREADY_REDEEMED req_…
}

API

SDK methods
MethodEndpoint
redemptions.reserve(body, opts)POST /v1/redemptions
redemptions.capture(id, opts)POST /v1/redemptions/:id/capture
redemptions.cancel(id, opts)POST /v1/redemptions/:id/cancel
redemptions.get(id) · list(query)GET /v1/redemptions[/:id]
checkoutSessions.create(body, opts) · get(id)/v1/checkout-sessions
redeemSessions.create(body, opts)POST /v1/redeem-sessions
sandbox.createVoucher(body, opts)POST /v1/sandbox/vouchers
settlements.list()GET /v1/settlements
verifyWebhook(raw, header, secrets)Verify and parse a webhook (throws WebhookSignatureError)
signRequest(…) · secretFromKey(key)Low-level signing helpers

opts is { idempotencyKey?, customerIp? }. Pass baseUrl: 'http://localhost:3003/v1' for the local stack.

Without the SDK

webhook handler without the SDK (Node)
// Express: verify before parsing. The signature covers the RAW body.
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(rawBody, header, secret, tolerance = 300) {
  const parts = (header ?? '').split(',').map((p) => p.trim().split('='));
  const t = Number(parts.find(([k]) => k === 't')?.[1]);
  const sigs = parts.filter(([k]) => k === 'v1').map(([, v]) => Buffer.from(v ?? '', 'hex'));
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > tolerance) throw new Error('stale or malformed signature');
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  if (!sigs.some((s) => s.length === expected.length && timingSafeEqual(s, expected))) throw new Error('bad signature');
  return JSON.parse(rawBody);
}

const app = express();
app.post('/hooks/vouchereto', express.raw({ type: 'application/json' }), async (req, res) => {
  let event;
  try { event = verifyWebhook(req.body.toString('utf8'), req.get('Vouchereto-Signature'), process.env.VOUCHERETO_WEBHOOK_SECRET); }
  catch { return res.sendStatus(400); }
  // Deliveries can repeat: skip events you already processed (event.id === Vouchereto-Event-Id header)
  // if (await seen(event.id)) return res.sendStatus(200);
  if (event.type === 'redemption.captured') { /* credit event.data.customer_ref with event.data.amount_minor */ }
  res.sendStatus(200);                       // answer within 10 s; do slow work in a queue
});

Scripts in our repository

Sandbox scripts
ScriptDoes
scripts/sandbox/quickstart.tsRuns the whole quickstart against a sandbox (smoke test of your key and network path)
scripts/sandbox/seed-demo-merchant.tsLocal stack: creates a demo sandbox merchant, test key and webhook endpoint
tests/docs/run-samples.tsExecutes every code sample of these docs against an API
Other languages: the OpenAPI file works with openapi-generator for Java, C#, Go, Python, PHP and more. Generated clients do not sign requests on their own; add the signature as a request interceptor using the recipe on the authentication page.