Skip to content

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.

  • 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.

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 webshop

If your webhook is unreachable or returns an error, RE-ZIP falls back to any available static pool for that shop.

The visibility field controls how vouchers from your pool are distributed:

VisibilityMeaning
privateOnly your own customers receive these vouchers. When a customer scans packaging belonging to your shop, they get a voucher from this pool.
publicAvailable as a fallback for customers of other shops that don’t have their own vouchers.
private_publicBoth — 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.

Create a pool with pool_type set to on_demand and provide your webhook_url:

Terminal window
$ 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.

When a customer earns a voucher, RE-ZIP sends a POST request to your webhook_url:

POST /rezip/voucher HTTP/1.1
Host: your-server.example.com
Content-Type: application/json
Webhook-Version: 1
Signature-Input: sig1=("@method" "@target-uri" "content-type");keyid="key-a1b2c3d4";alg="ecdsa-p384-sha384";created=1700000000
Signature: 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).

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"
}
FieldTypeDescription
codestringThe voucher code the customer will see
linkstringA redirect URL — the customer is taken here to redeem the voucher on your webshop
typestringDiscount type: amount, percentage, or gift
valueintegerDiscount value — in minor units for amount (e.g. 5000 for 50 DKK), whole number for percentage (e.g. 15), quantity for gift
unitstringISO 4217 currency code for amount (e.g. "DKK", "EUR"), "%" for percentage, or item description for gift
conditionsstringFree-text conditions shown to the customer
expires_atstringISO 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.

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.

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.

List all pools for your shop:

Terminal window
$ curl -s \
-H 'Accept-Version: 2.0' \
-H 'Authorization: Bearer $TOKEN' \
https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/ | pp.json

Get a specific pool:

Terminal window
$ curl -s \
-H 'Accept-Version: 2.0' \
-H 'Authorization: Bearer $TOKEN' \
https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/$POOL_ID | pp.json

Delete a pool:

Terminal window
$ curl -s -X DELETE \
-H 'Accept-Version: 2.0' \
-H 'Authorization: Bearer $TOKEN' \
https://api.re-zip.com/shops/$SHOP_ID/vouchers/pools/$POOL_ID
ActionMethodEndpoint
Create poolPOST/shops/{id}/vouchers/pools
List poolsGET/shops/{id}/vouchers/pools/
Get poolGET/shops/{id}/vouchers/pools/{pool_id}
Delete poolDELETE/shops/{id}/vouchers/pools/{pool_id}
DirectionRE-ZIP → your server
MethodPOST
URLYour webhook_url
Request body{ "voucher_pool_id": "<ULID>" }
Request headerWebhook-Version: 1
Expected response2xx with { code, link, type, value, unit, conditions, expires_at }
On failureRE-ZIP falls back to static pools