Coupons
Coupons are a core concept for the Wallet App platform, Coupons are used as a 1 time use code that can be redeemed for a discount on a product or service in the real world. Coupons can be created and managed through the API.
The coupons model
The coupon model contains the information about a coupon. Such as the webshop pricing, when and for how long it should be active and the look and feel in the Apple or Android passwallet. The following properties are available:
Properties
- Name
identifier- Type
- string
- Description
Unique identifier for the coupon.
- Name
price- Type
- float
- Description
The sale price of the coupon.
- Name
points- Type
- integer
- Description
The sale amount of points of the coupon.
- Name
expiration_label- Type
- string
- Description
Label for the
active, blocked, expiredstates of the coupon in the apple wallet.
- Name
active_label- Type
- string
- Description
The text in the apple wallet for once a coupon is active.
- Name
blocked_label- Type
- string
- Description
The text in the apple wallet for once a coupon is blocked.
- Name
expired_label- Type
- string
- Description
The text shown in the apple wallet once a pass has expired.
- Name
redeemed_label- Type
- string
- Description
The text shown in the apple wallet once a pass has been redeemed.
- Name
show_title- Type
- boolean
- Description
whether or not to show the title in the apple wallet.
- Name
show_subtitle- Type
- boolean
- Description
whether or not to show the subtitle in the apple wallet.
- Name
organization- Type
- object
- Description
Contains in formation about the coupons organization. contains:
['identifier', 'name']
- Name
primary_color- Type
- string
- Description
A Hexadecimal color code for the primary color of the coupon (background color for the apple wallet pass).
- Name
text_color- Type
- string
- Description
A Hexadecimal color code for the text color of the coupon (text color for the apple wallet pass).
- Name
label_color- Type
- string
- Description
A Hexadecimal color code for the label color of the coupon (label color for the apple wallet pass).
- Name
title- Type
- string
- Description
The title of the coupon.
- Name
subtitle- Type
- string
- Description
The subtitle of the coupon.
- Name
thumbnail_url- Type
- string|url
- Description
The URL to the image of the coupon.
- Name
start_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
end_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
use_start_and_end- Type
- boolean
- Description
whether or not to use a start and end date in the webshop.
- Name
stock- Type
- integer
- Description
Amount of coupons available for sale or usage.
- Name
use_stock- Type
- boolean
- Description
whether or not to use a stock in the webshop.
- Name
created_at- Type
- timestamp
- Description
A timestamp when the coupon was created.
- Name
updated_at- Type
- timestamp
- Description
A timestamp when the coupon was last updated.
Note
Because a user can have access to multiple organizations we require a organization_id to be provided for most requests. We will also return an organization reference in each response.
List all coupons
This endpoint returns a list of all the coupons the currently authenticated user has access to.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.get- Type
- string
- Description
The scope required to access this endpoint.
Request
curl -X GET https://api.walletapp.co/coupons
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
Response
{
"message": "Request successful",
"data": [
{
"active_label": "ongebruikt",
"blocked_label": "geblokkeerd",
"created_at": "<timestamp>",
"end_date": "<timestamp>",
"expiration_days": 30,
"expiration_label": "Geldig t/m",
"expired_label": "verlopen",
"identifier": "<UUID>",
"organization": {
"name": "<organization_name>",
"identifier": "<UUID>"
},
"points": 0,
"price": 0,
"primary_color": "#ffffff",
"redeemed_label": "ingeleverd",
"show_subtitle": 0,
"show_title": 0,
"start_date": "<timestamp>",
"stock": 0,
"subtitle": "<subtitle>",
"text_color": "#000000",
"thumbnail_url": "<url>",
"title": "<title>",
"updated_at": "2022-12-23T09:59:31.000000Z",
"use_start_and_end": 0,
"use_stock": 0
},
],
...
}
Create a coupon
This endpoint allows you to create a coupon.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.store- Type
- string
- Description
The scope required to access this endpoint.
Request body
- Name
title- Type
- string|Required
- Description
The title of the coupon.
- Name
subtitle- Type
- string|Required
- Description
The subtitle of the coupon.
- Name
organization_id- Type
- string|Required
- Description
The organization id the coupon should be created for.
- Name
description- Type
- string
- Description
The webshop description of a coupon.
- Name
thumbnail_url- Type
- string|url
- Description
A valid url to the thumbnail image of a coupon.
- Name
price- Type
- float
- Description
The webshop price of a coupon.
- Name
external_reference- Type
- string
- Description
An external reference returned with each pass
- Name
points- Type
- integer
- Description
The webshop amount of points for a coupon.
- Name
expiration_label- Type
- string
- Description
The text shown in the apple wallet for the expiration date.
- Name
expiration_days- Type
- integer
- Description
The amount of days the associated pass will be valid for after creation.
- Name
blocked_label- Type
- string
- Description
The text in the apple wallet for once a coupon is blocked.
- Name
expired_label- Type
- string
- Description
The text shown in the apple wallet once a pass has expired.
- Name
active_label- Type
- string
- Description
The text shown in the apple wallet once a pass is active.
- Name
redeemed_label- Type
- string
- Description
The text shown in the apple wallet once a pass has been redeemed.
- Name
show_title- Type
- boolean
- Description
whether or not to show the title in the apple wallet.
- Name
show_subtitle- Type
- boolean
- Description
whether or not to show the subtitle in the apple wallet.
- Name
primary_color- Type
- string
- Description
A Hexadecimal color code for the primary color of the coupon (background color for the apple wallet pass).
- Name
text_color- Type
- string
- Description
A Hexadecimal color code for the text color of the coupon (text color for the apple wallet pass).
- Name
label_color- Type
- string
- Description
A Hexadecimal color code for the label color of the coupon (label color for the apple wallet pass).
- Name
stock- Type
- integer
- Description
Amount of coupons available for sale or usage.
- Name
use_stock- Type
- boolean
- Description
whether or not to use a stock in the webshop.
- Name
start_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
end_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
use_start_and_end- Type
- boolean
- Description
whether or not to use a start and end date in the webshop.
Request
curl -X POST https://api.walletapp.co/coupons
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
"title":"<your_title>",
"subtitle":"<your_subtitle>",
"description":"<your_description>",
"organization_id":"<organization_id>",
"thumbnail_url":"<image_url>",
"price": 1.50,
"expiration_label":"Pass status",
"expiration_days": 22,
"external_reference": "<string>",
"expired_label":"Expired",
"primary_color":"#ff00ff",
"text_color":"#ff0000",
"label_color":"#00ff00",
"show_title": true,
"show_subtitle": true,
"active_label":"Active",
"blocked_label":"Blocked",
"redeemed_label":"Redeemed",
"points": 12,
"use_stock": true,
"use_start_and_end": true,
"stock":15,
"start_date": "2022-01-01",
"end_date":"2023-01-01"
}'
Response
{
"message": "Request successful",
"data": {
"active_label": "ongebruikt",
"blocked_label": "geblokkeerd",
"created_at": "<timestamp>",
"end_date": "<timestamp>",
"expiration_days": 30,
"expiration_label": "Geldig t/m",
"expired_label": "verlopen",
"identifier": "<UUID>",
"organization": {
"name": "<organization_name>",
"identifier": "<UUID>"
},
"points": 0,
"price": 0,
"primary_color": "#ffffff",
"redeemed_label": "ingeleverd",
"show_subtitle": 0,
"show_title": 0,
"start_date": "<timestamp>",
"stock": 0,
"subtitle": "<subtitle>",
"text_color": "#000000",
"thumbnail_url": "<url>",
"title": "<title>",
"updated_at": "2022-12-23T09:59:31.000000Z",
"use_start_and_end": 0,
"use_stock": 0
}
}
Inspect a coupon
The endpoint allows you to fetch a coupon by its identifier.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.get- Type
- string
- Description
The scope required to access this endpoint.
Route parameters
- Name
identifier- Type
- string
- Description
The id of the coupon you want to update.
Request
curl -X GET https://api.walletapp.co/coupons/:identifier
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
Response
{
"message": "Request successful",
"data": {
"active_label": "ongebruikt",
"blocked_label": "geblokkeerd",
"created_at": "<timestamp>",
"end_date": "<timestamp>",
"expiration_days": 30,
"expiration_label": "Geldig t/m",
"expired_label": "verlopen",
"identifier": "<UUID>",
"organization": {
"name": "<organization_name>",
"identifier": "<UUID>"
},
"points": 0,
"price": 0,
"primary_color": "#ffffff",
"redeemed_label": "ingeleverd",
"show_subtitle": 0,
"show_title": 0,
"start_date": "<timestamp>",
"stock": 0,
"subtitle": "<subtitle>",
"text_color": "#000000",
"thumbnail_url": "<url>",
"title": "<title>",
"updated_at": "2022-12-23T09:59:31.000000Z",
"use_start_and_end": 0,
"use_stock": 0
}
}
Update a coupon
The endpoint allows you to modify a coupon
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.update- Type
- string
- Description
The scope required to access this endpoint.
Route parameters
- Name
identifier- Type
- string
- Description
The id of the coupon you want to update.
Request body
- Name
title- Type
- string
- Description
The title of the coupon.
- Name
subtitle- Type
- string
- Description
The subtitle of the coupon.
- Name
organization_id- Type
- string
- Description
The organization id the coupon should be updated to.
- Name
description- Type
- string
- Description
The webshop description of a coupon.
- Name
thumbnail_url- Type
- string|url
- Description
A valid url to the thumbnail image of a coupon.
- Name
price- Type
- float
- Description
The webshop price of a coupon.
- Name
points- Type
- integer
- Description
The webshop amount of points for a coupon.
- Name
expiration_label- Type
- string
- Description
The text shown in the apple wallet for the expiration date.
- Name
expiration_days- Type
- integer
- Description
The amount of days the associated pass will be valid for after creation.
- Name
blocked_label- Type
- string
- Description
The text in the apple wallet for once a coupon is blocked.
- Name
expired_label- Type
- string
- Description
The text shown in the apple wallet once a pass has expired.
- Name
active_label- Type
- string
- Description
The text shown in the apple wallet once a pass is active.
- Name
redeemed_label- Type
- string
- Description
The text shown in the apple wallet once a pass has been redeemed.
- Name
show_title- Type
- boolean
- Description
whether or not to show the title in the apple wallet.
- Name
show_subtitle- Type
- boolean
- Description
whether or not to show the subtitle in the apple wallet.
- Name
primary_color- Type
- string
- Description
A Hexadecimal color code for the primary color of the coupon (background color for the apple wallet pass).
- Name
text_color- Type
- string
- Description
A Hexadecimal color code for the text color of the coupon (text color for the apple wallet pass).
- Name
label_color- Type
- string
- Description
A Hexadecimal color code for the label color of the coupon (label color for the apple wallet pass).
- Name
stock- Type
- integer
- Description
Amount of coupons available for sale or usage.
- Name
use_stock- Type
- boolean
- Description
whether or not to use a stock in the webshop.
- Name
start_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
end_date- Type
- timestamp
- Description
Used to set the availablity of the coupon in the webshop.
- Name
use_start_and_end- Type
- boolean
- Description
whether or not to use a start and end date in the webshop.
Request
curl -X PUT https://api.walletapp.co/coupons/:identifier
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
"title":"<your_title>",
"subtitle":"<your_subtitle>",
"description":"<your_description>",
"organization_id":"<organization_id>",
"thumbnail_url":"<image_url>",
"price": 1.50,
"expiration_label":"Pass status",
"expiration_days": 22,
"expired_label":"Expired",
"primary_color":"#ff00ff",
"text_color":"#ff0000",
"label_color":"#00ff00",
"show_title": true,
"show_subtitle": true,
"active_label":"Active",
"blocked_label":"Blocked",
"redeemed_label":"Redeemed",
"points": 12,
"use_stock": true,
"use_start_and_end": true,
"stock":15,
"start_date": "2022-01-01",
"end_date":"2023-01-01"
}'
Response
{
"message": "Request successful",
"data": {
"active_label": "ongebruikt",
"blocked_label": "geblokkeerd",
"created_at": "<timestamp>",
"end_date": "<timestamp>",
"expiration_days": 30,
"expiration_label": "Geldig t/m",
"expired_label": "verlopen",
"identifier": "<UUID>",
"organization": {
"name": "<organization_name>",
"identifier": "<UUID>"
},
"points": 0,
"price": 0,
"primary_color": "#ffffff",
"redeemed_label": "ingeleverd",
"show_subtitle": 0,
"show_title": 0,
"start_date": "<timestamp>",
"stock": 0,
"subtitle": "<subtitle>",
"text_color": "#000000",
"thumbnail_url": "<url>",
"title": "<title>",
"updated_at": "2022-12-23T09:59:31.000000Z",
"use_start_and_end": 0,
"use_stock": 0
}
}
Delete a coupon
The endpoint allows you to delete a coupon.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.delete- Type
- string
- Description
The scope required to access this endpoint.
Route parameters
- Name
identifier- Type
- string
- Description
The id of the coupon you want to update.
Request
curl -X delete https://api.walletapp.co/coupons/:identifier
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
Response
{
"message": "Request successful",
"data": "Coupon Archived",
}
Restore a coupon
The endpoint allows you to restore a coupon.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.delete- Type
- string
- Description
The scope required to access this endpoint.
Route parameters
- Name
identifier- Type
- string
- Description
The id of the coupon you want to update.
Request
curl -X PATCH https://api.walletapp.co/coupons/:identifier
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
Response
{
"message": "Request successful",
"data": "Coupon Restored",
}
Redeem a points reward
Redeems a coupon that costs points for a member in one atomic step: the points are deducted from the member's storecard, a coupon pass is issued to the member already marked as redeemed, and the stock goes down by one. Either all of it happens or none of it does; there is no state where points are gone but no reward was registered.
The call is idempotent on reference: sending the same reference again changes nothing and returns was_duplicate: true with the original result (200), so it is safe to retry after a timeout. A reference is unique per coupon; reusing it for another member or storecard returns 409. Re-sending the reference of a reversed redemption returns the original (reversed) result and deducts nothing; to redeem again, use a new reference.
The points are deducted as a normal points transaction (reference redeem-<reference>, description Reward: <coupon title>), so it appears in the points history and in GET /storecards/:identifier/transactions. If you have a webhook subscription you receive points.changed, coupon.issued and coupon.redeemed once each, after the redemption was committed.
Which storecard? The points come from the member's storecard with points in the organization of the coupon. When the member has exactly one, it is used. When the member has several, storecard_id is required (422, storecard_required, the response lists the candidates). Points are never split over several cards.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
- Name
Idempotency-Key- Type
- string|optional
- Description
Optional. When sent it must equal
reference, otherwise the call is rejected with422. The general Idempotency-Key behavior applies on top: the same key with the same body within 24 hours replays the first response (201, withIdempotent-Replayed: true) without running again, and the same key with a different body returns422. A replay shows the state of the first response; without the header,referencealone returns the current state (200,was_duplicate: true).
Abilities
- Name
coupons.store- Type
- string
- Description
Required.
- Name
storecards.store- Type
- string
- Description
Required as well, because points are deducted. Without it the call returns
403.
Route parameters
- Name
identifier- Type
- string
- Description
The identifier of the coupon. It must belong to the organization of your token, otherwise
404.
Body parameters
- Name
member_id- Type
- string|Required
- Description
The member's UUID (
member_idas returned by the members endpoints).
- Name
reference- Type
- string|Required
- Description
Your own unique id for this redemption, for example a till receipt number (max 56 characters).
- Name
storecard_id- Type
- string|optional
- Description
The identifier of the storecard to take the points from. Only needed when the member has more than one storecard with points.
Response fields
- Name
redemption_id- Type
- integer
- Description
Use this to reverse the redemption.
- Name
status- Type
- string
- Description
redeemedorreversed.
- Name
pass_id- Type
- string
- Description
The UUID of the coupon pass that was issued to the member.
- Name
points_spent- Type
- integer
- Description
The coupon's points price.
- Name
points_balance- Type
- number
- Description
The member's points on that storecard after the redemption.
- Name
stock- Type
- integer|null
- Description
Remaining stock, or
nullwhen the coupon does not use stock management.
Errors
- Name
403- Type
- status
- Description
The token misses
storecards.store.
- Name
404- Type
- status
- Description
coupon_not_found,member_not_found(also for a member of another organization) orstorecard_not_found.
- Name
409- Type
- status
- Description
reference_conflict: the reference was used for another member or storecard.
- Name
422- Type
- status
- Description
insufficient_points,out_of_stock,coupon_unavailable(archived, no points price, outside its start and end date, or a voucher type that needs an external code),storecard_required,pass_blocked/pass_disabled, or a validation error.
Request
curl -X POST https://api.walletapp.co/coupons/cpn_free_coffee/redemptions
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
"member_id": "0b7d5c1e-3f0a-4d51-9a64-2f5c1d8e7a10",
"reference": "till-2026-10-09-0042"
}'
Response (201 Created)
{
"data": {
"redemption_id": 318,
"reference": "till-2026-10-09-0042",
"was_duplicate": false,
"status": "redeemed",
"coupon_id": "cpn_free_coffee",
"member_id": "0b7d5c1e-3f0a-4d51-9a64-2f5c1d8e7a10",
"storecard_id": "sc_8f3a1c",
"pass_id": "5e0c9a52-7b1d-4a6e-8f33-0c2d9b6a41e7",
"points_spent": 40,
"points_balance": 60,
"points_transaction_id": 1051,
"reversal_transaction_id": null,
"stock": 24,
"redeemed_at": "2026-10-09T14:12:05+02:00",
"reversed_at": null
}
}
Response on a retry (200 OK)
{
"data": {
"redemption_id": 318,
"was_duplicate": true,
"status": "redeemed",
"...": "same as the original response"
}
}
Not enough points (422)
{
"error": "insufficient_points",
"message": "The member does not have enough points"
}
Several storecards (422)
{
"error": "storecard_required",
"message": "The member has several storecards with points: pass storecard_id",
"storecards": ["sc_8f3a1c", "sc_2d77e0"]
}
Reverse a redemption
Reverses a redemption exactly once and atomically: the points are refunded (a compensating points transaction with the reference reverse-redeem-<reference>, see Reverse a transaction), the stock is restored, and the coupon pass that was issued is voided.
A voided pass is no longer counted as redeemed and is blocked, so the member cannot use the reward after getting the points back. A second reversal returns 409 (already_reversed) and changes nothing. The original reference stays used: repeating the original redeem call returns the redemption with status: reversed and does not redeem again; use a new reference to redeem again.
Required headers
- Name
Authorization- Type
- string
- Description
The token itself as a bearer token.
Abilities
- Name
coupons.store- Type
- string
- Description
Required.
- Name
storecards.store- Type
- string
- Description
Required as well, because points are refunded.
Route parameters
- Name
identifier- Type
- string
- Description
The identifier of the coupon of the redemption.
- Name
id- Type
- integer
- Description
The
redemption_idreturned when the reward was redeemed.
Errors
- Name
404- Type
- status
- Description
coupon_not_foundorredemption_not_found.
- Name
409- Type
- status
- Description
already_reversed.
- Name
422- Type
- status
- Description
pass_blocked/pass_disabled: the member's storecard pass was blocked since, so points cannot be refunded; nothing is changed.
Request
curl -X POST https://api.walletapp.co/coupons/cpn_free_coffee/redemptions/318/reverse
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
Response (200 OK)
{
"data": {
"redemption_id": 318,
"reference": "till-2026-10-09-0042",
"was_duplicate": false,
"status": "reversed",
"coupon_id": "cpn_free_coffee",
"member_id": "0b7d5c1e-3f0a-4d51-9a64-2f5c1d8e7a10",
"storecard_id": "sc_8f3a1c",
"pass_id": "5e0c9a52-7b1d-4a6e-8f33-0c2d9b6a41e7",
"points_spent": 40,
"points_balance": 100,
"points_transaction_id": 1051,
"reversal_transaction_id": 1052,
"stock": 25,
"redeemed_at": "2026-10-09T14:12:05+02:00",
"reversed_at": "2026-10-09T14:20:41+02:00"
}
}
Response on a second reversal (409)
{
"error": "already_reversed",
"message": "Redemption was already reversed",
"redemption_id": 318,
"reversed_at": "2026-10-09T14:20:41+02:00"
}
