Voucher integration
This guide explains how to set up an on-demand voucher pool so that RE-ZIP generates vouchers dynamically by calling your server, instead of drawing from a pre-uploaded pool of codes. This is the recommended integration path for engineering teams that want full control over voucher generation — your system decides the code, the redirect link, the discount, and the conditions at the moment a customer earns a reward.
Prerequisites
Section titled “Prerequisites”- A RE-ZIP shop account with a valid access token (see Getting started)
- A publicly reachable HTTPS endpoint on your side that can receive webhook requests from RE-ZIP (see Webhooks for general webhook setup, signature verification, and retry behaviour)
Throughout this guide $SHOP_ID refers to your shop’s ULID and $TOKEN is
your Bearer token.
How it works
Section titled “How it works”With a static pool (the default), you upload a fixed set of voucher codes in advance and RE-ZIP draws from that pool when a customer scans packaging.
With an on-demand pool, there are no pre-uploaded codes. Instead, the pool
carries a webhook_url pointing at your server. When a customer scans RE-ZIP
packaging at a drop point, RE-ZIP calls your webhook and your server responds
with a freshly generated voucher — code, redirect link, discount details, and
all.
Customer scans packaging │ ▼RE-ZIP finds on-demand pool for shop + country │ ▼RE-ZIP POSTs to your webhook_url │ ▼Your server generates a voucher and responds │ ▼Customer receives the voucher with a redirect link to your webshopIf your webhook is unreachable or returns an error, RE-ZIP falls back to any available static pool for that shop.
Pool visibility
Section titled “Pool visibility”The visibility field controls how vouchers from your pool are distributed:
| Visibility | Meaning |
|---|---|
private | Only your own customers receive these vouchers. When a customer scans packaging belonging to your shop, they get a voucher from this pool. |
public | Available as a fallback for customers of other shops that don’t have their own vouchers. |
private_public | Both — your customers get these vouchers, and they are also available as a fallback for the wider public. |
For most integrations you will want private or private_public.
Creating an on-demand pool
Section titled “Creating an on-demand pool”Create a pool with pool_type set to on_demand and provide your
webhook_url:
$ curl -s -X POST \ -H 'Accept-Version: 2.0' \ -H 'Authorization: Bearer $TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "name": "Webshop voucher generation", "countries": ["DK", "DE"], "visibility": "private", "pool_type": "on_demand", "webhook_url": "https://your-server.example.com/rezip/voucher" }' \ https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools | pp.json{ "id": "01JAYSCS07SVDQANFS61TF0XJN", "name": "Webshop voucher generation", "countries": ["DK", "DE"], "visibility": "private", "pool_type": "on_demand", "webhook_url": "https://your-server.example.com/rezip/voucher", "created_at": "2026-04-22T10:00:00.000Z", "updated_at": "2026-04-22T10:00:00.000Z"}The webhook_url is required when pool_type is on_demand. Omitting it
returns a 400 validation error.
On-demand pools do not use batches or pre-uploaded codes. Attempting to create a batch on an on-demand pool will return
400.
Implementing the webhook
Section titled “Implementing the webhook”When a customer earns a voucher, RE-ZIP sends a POST request to your
webhook_url:
Request
Section titled “Request”POST /rezip/voucher HTTP/1.1Host: your-server.example.comContent-Type: application/jsonWebhook-Version: 1Signature-Input: sig1=("@method" "@target-uri" "content-type");keyid="key-a1b2c3d4";alg="ecdsa-p384-sha384";created=1700000000Signature: sig1=:BASE64_ENCODED_SIGNATURE:
{ "voucher_pool_id": "01JAYSCS07SVDQANFS61TF0XJN"}Like all RE-ZIP webhooks, the request is signed using
HTTP Message Signatures (RFC 9421).
See the Webhooks guide for how to verify the Signature
and Signature-Input headers.
The voucher_pool_id identifies which pool the request is for, useful if you
have multiple on-demand pools configured (e.g. one per country or campaign).
Response
Section titled “Response”Your server must respond with a 2xx status and a JSON body containing the
voucher details:
{ "code": "SHOP-A1B2C3", "link": "https://shop.example.com/redeem?code=SHOP-A1B2C3", "type": "percentage", "value": 15, "unit": "%", "conditions": "Min. purchase 200 DKK", "expires_at": "2027-01-01T00:00:00.000Z"}| Field | Type | Description |
|---|---|---|
code | string | The voucher code the customer will see |
link | string | A redirect URL — the customer is taken here to redeem the voucher on your webshop |
type | string | Discount type: amount, percentage, or gift |
value | integer | Discount value — in minor units for amount (e.g. 5000 for 50 DKK), whole number for percentage (e.g. 15), quantity for gift |
unit | string | ISO 4217 currency code for amount (e.g. "DKK", "EUR"), "%" for percentage, or item description for gift |
conditions | string | Free-text conditions shown to the customer |
expires_at | string | ISO 8601 expiry timestamp |
See the Vouchers guide for a detailed breakdown of how each field is displayed to the customer.
The link field is what makes this powerful for deep integrations — when the
customer receives the voucher, they are taken directly to your webshop with
the discount ready to apply, rather than having to copy-paste a code manually.
Error handling
Section titled “Error handling”If your webhook returns a non-2xx status, times out, or returns an invalid response body, RE-ZIP logs the failure and falls back to any available static voucher pool for that shop. The customer still receives a reward — just not one generated by your webhook.
This means you do not need 100% uptime on your webhook endpoint. However, you should monitor your error rate and aim for reliable responses since on-demand vouchers are typically more valuable to your customers than the static fallback.
Example implementation
Section titled “Example implementation”A Node.js webhook handler with signature verification (see Webhooks for details on verifying signatures):
import { randomBytes } from "node:crypto"import { verifySignature } from "./rezip-webhook-verify.js" // your verification helper
app.post("/rezip/voucher", async (req, res) => { // Verify the request signature — see the Webhooks guide const isValid = await verifySignature(req) if (!isValid) { return res.status(401).json({ error: "Invalid signature" }) }
const { voucher_pool_id } = req.body
const code = `SHOP-${randomBytes(4).toString("hex").toUpperCase()}` const link = `https://shop.example.com/redeem?code=${code}`
// Create the discount code in your e-commerce system here // ...
res.json({ code, link, type: "amount", value: 5000, // minor units — 5000 = 50.00 DKK unit: "DKK", conditions: "Min. purchase 200 DKK", expires_at: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), })})In a real integration you would generate the code in your e-commerce platform (Shopify, WooCommerce, custom system, etc.) and return its details. The key point is that the code and redirect link are created at the moment the customer earns the reward, not ahead of time.
Managing pools
Section titled “Managing pools”List all pools for your shop:
$ curl -s \ -H 'Accept-Version: 2.0' \ -H 'Authorization: Bearer $TOKEN' \ https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/ | pp.jsonGet a specific pool:
$ curl -s \ -H 'Accept-Version: 2.0' \ -H 'Authorization: Bearer $TOKEN' \ https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/$POOL_ID | pp.jsonDelete a pool:
$ curl -s -X DELETE \ -H 'Accept-Version: 2.0' \ -H 'Authorization: Bearer $TOKEN' \ https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/$POOL_IDAPI reference
Section titled “API reference”Pool endpoints
Section titled “Pool endpoints”| Action | Method | Endpoint |
|---|---|---|
| Create pool | POST | /shops/{id}/vouchers/pools |
| List pools | GET | /shops/{id}/vouchers/pools/ |
| Get pool | GET | /shops/{id}/vouchers/pools/{pool_id} |
| Delete pool | DELETE | /shops/{id}/vouchers/pools/{pool_id} |
Webhook contract
Section titled “Webhook contract”| Direction | RE-ZIP → your server |
| Method | POST |
| URL | Your webhook_url |
| Request body | { "voucher_pool_id": "<ULID>" } |
| Request header | Webhook-Version: 1 |
| Expected response | 2xx with { code, link, type, value, unit, conditions, expires_at } |
| On failure | RE-ZIP falls back to static pools |