Rewards
Endpoints for managing the loyalty rewards catalog and the redemption lifecycle. Rewards are spendable loyalty items (optionally costing wallet points and optionally issuing a coupon code); redemptions track each time a client claims a reward, including points spent, coupon assignment, and used/cancelled state.
Auth: All endpoints are workspace-scoped and require a Bearer token belonging to a member of the target workspace (enforced by createWorkspaceGuard resolving the workspace from the path). Rewards and redemptions loaded by id are additionally re-checked against the caller's workspace access inside the service.
List Rewards
GET /wallethero-api/workspace/:workspaceId/rewards
Returns the workspace's rewards catalog, ordered by sort_order ascending then date_created descending. Supports filtering by active/purchasable state and pagination.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
active | boolean ("true"/"false") | No | — (no filter) | Filter to active or inactive rewards |
purchasable | boolean ("true"/"false") | No | — (no filter) | Filter to client-purchasable rewards |
limit | number | No | 50 | Max rewards to return |
offset | number | No | 0 | Pagination offset |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards?active=true&limit=20" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "8a1f...",
"workspace_id": "WORKSPACE_ID",
"name": "Free Coffee",
"description": "One free coffee",
"active": true,
"purchasable": true,
"cost_in_points": 100,
"wallet_type_id": "c2b9...",
"monetary_value": 5,
"code_pattern": "COFFEE-{RAND:6}",
"availability_start": null,
"availability_end": null,
"visibility_tiers": [],
"visibility_segments": [],
"usage_limit": null,
"usage_limit_per_client": 1,
"categories": ["drinks"],
"image": null,
"sort_order": 0,
"notification_config": { "send_on_redemption": true, "body": "Enjoy your {{reward_name}}!" }
}
],
"meta": { "total_count": 12, "returned_count": 1, "offset": 0 }
}SDK
const { data } = await wh.rewards.list(workspaceId, { active: true, purchasable: true });Create Reward
POST /wallethero-api/workspace/:workspaceId/rewards
Creates a new reward in the workspace. If wallet_type_id is not set, purchasable is forced to false (a wallet-less reward cannot deduct points).
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier (injected as workspace_id) |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string (1–255) | Yes | — | Reward display name |
cost_in_points | number (≥ 0) | Yes | — | Points cost to redeem |
description | string | No | — | Reward description |
code_pattern | string | null | No | — | Coupon code template; must contain at least one {RAND:N} token with N >= 4 (e.g. COFFEE-{RAND:6}) |
active | boolean | No | true | Whether the reward is active |
wallet_type_id | string (UUID) | null | No | — | Wallet whose points are deducted on redemption |
monetary_value | number (≥ 0) | null | No | — | Cash-equivalent value of the reward |
availability_start | string (ISO) | null | No | — | Earliest date the reward can be redeemed |
availability_end | string (ISO) | null | No | — | Latest date the reward can be redeemed |
visibility_tiers | string[] (UUID) | No | [] | Tier IDs the reward is visible to (empty = all tiers) |
visibility_segments | string[] (UUID) | No | [] | Segment IDs the reward is visible to |
usage_limit | number (int ≥ 0) | null | No | — | Global redemption cap across all clients |
usage_limit_per_client | number (int ≥ 0) | null | No | — | Per-client redemption cap |
categories | string[] | No | [] | Category tags |
image | string | null | No | — | Image URL/reference |
sort_order | number (int) | No | 0 | Ordering weight in lists |
purchasable | boolean | No | true | Whether clients can self-redeem (forced false if no wallet_type_id) |
notification_config | object | null | No | — | { send_on_redemption?: boolean, body: string } — wallet notification on redemption (body supports template tokens) |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Free Coffee",
"cost_in_points": 100,
"wallet_type_id": "c2b9...",
"code_pattern": "COFFEE-{RAND:6}",
"usage_limit_per_client": 1
}'Response (200)
{ "data": { "id": "8a1f...", "name": "Free Coffee", "cost_in_points": 100, "active": true, "purchasable": true } }SDK
const { data: reward } = await wh.rewards.create(workspaceId, {
name: "Free Coffee",
cost_in_points: 100,
wallet_type_id: "c2b9...",
});Redeem Reward
POST /wallethero-api/workspace/:workspaceId/rewards/redeem
Redeems a reward for a client. Validates that the client belongs to the reward's workspace, the reward is active and within its availability window, tier visibility allows it, and usage limits are not exceeded. Deducts points (unless skip_points_cost is set or the reward has no wallet/zero cost), assigns a coupon code if a code_pattern is configured, records a redemption with status fulfilled, emits a reward_redeemed client event, queues a wallet push, and optionally sends a notification.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
reward_id | string (UUID) | Yes | — | Reward to redeem |
client_id | string (UUID) | Yes | — | Client redeeming the reward |
source | enum | No | admin | One of admin, self_serve, earning_rule, mobile, campaign |
metadata | object | No | — | Arbitrary metadata stored on the redemption |
skip_points_cost | boolean | No | false | If true, no points are deducted and global/per-client usage limits are bypassed |
Note:
send_notificationis honored by the service (defaults totrue) but is not part of the validated redeem schema; the notification only fires when the reward'snotification_config.send_on_redemptionandbodyare set.
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redeem" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reward_id": "8a1f...",
"client_id": "44d0...",
"source": "self_serve"
}'Response (200)
{
"data": {
"redemption": {
"id": "9f3a...",
"reward_id": "8a1f...",
"client_id": "44d0...",
"workspace_id": "WORKSPACE_ID",
"points_spent": 100,
"points_ledger_id": "led_...",
"coupon_id": "cpn_...",
"status": "fulfilled",
"redemption_date": "2026-06-16T10:00:00.000Z",
"source": "self_serve"
},
"coupon": { "id": "cpn_...", "code": "COFFEE-A1B2C3", "status": "issued" },
"points_ledger_id": "led_..."
}
}SDK
const { data } = await wh.rewards.redeem(workspaceId, {
reward_id: "8a1f...",
client_id: "44d0...",
source: "self_serve",
});List Redemptions
GET /wallethero-api/workspace/:workspaceId/rewards/redemptions
Lists redemptions in the workspace, newest first, with coupon code/status joined in. Supports filtering and pagination.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
reward_id | string (UUID) | No | — | Filter by reward |
client_id | string (UUID) | No | — | Filter by client |
status | enum | No | — | pending, fulfilled, cancelled, or expired |
source | enum | No | — | admin, self_serve, earning_rule, or mobile |
from_date | string (ISO) | No | — | Redemption date lower bound (inclusive) |
to_date | string (ISO) | No | — | Redemption date upper bound (inclusive) |
limit | number (1–500) | No | 50 | Max redemptions to return |
offset | number (≥ 0) | No | 0 | Pagination offset |
A
workspace_idquery field is also accepted by the schema but the path workspace is authoritative.
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions?status=fulfilled&limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "9f3a...",
"reward_id": "8a1f...",
"client_id": "44d0...",
"points_spent": 100,
"status": "fulfilled",
"coupon_code": "COFFEE-A1B2C3",
"coupon_status": "issued",
"source": "self_serve",
"redemption_date": "2026-06-16T10:00:00.000Z"
}
],
"meta": { "total_count": 240, "returned_count": 1, "offset": 0 }
}SDK
const { data } = await wh.rewards.listRedemptions(workspaceId, { status: "fulfilled" });
// or scoped to a client:
const clientRedemptions = await wh.rewards.getClientRedemptions(workspaceId, clientId);Top Redeemed Rewards
POST /wallethero-api/workspace/:workspaceId/rewards/redemptions/top
Returns the most-redeemed rewards in the workspace (grouped by reward), ordered by redemption count, plus overall totals. Filters are passed in the request body.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
from_date | string (ISO) | No | — | Redemption date lower bound |
to_date | string (ISO) | No | — | Redemption date upper bound |
status | enum | No | — | pending, fulfilled, cancelled, or expired |
exclude_cancelled | boolean | No | — | When no status given, excludes cancelled redemptions |
limit | number | No | 5 | Number of top rewards (clamped to 1–50) |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/top" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "exclude_cancelled": true, "limit": 10 }'Response (200)
{
"data": {
"rewards": [
{ "reward_id": "8a1f...", "reward_name": "Free Coffee", "redemption_count": 132, "points_spent": 13200 }
],
"total_count": 240,
"total_points_spent": 51000
}
}SDK
const { data } = await wh.rewards.getTopRedeemedRewards(workspaceId, {
exclude_cancelled: true,
limit: 10,
});Aggregate Redemptions
POST /wallethero-api/workspace/:workspaceId/rewards/redemptions/aggregate
Aggregates redemption counts and total points spent across the workspace, optionally bucketed over time. Filters are passed in the request body.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
from_date | string (ISO) | No | — | Redemption date lower bound |
to_date | string (ISO) | No | — | Redemption date upper bound |
status | enum | No | — | pending, fulfilled, cancelled, or expired |
exclude_cancelled | boolean | No | — | When no status given, excludes cancelled redemptions |
group_by_time | enum | No | — | day, week, or month; when set, returns time buckets |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/aggregate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "group_by_time": "month", "exclude_cancelled": true }'Response (200)
{
"data": {
"count": 240,
"points_spent": 51000,
"buckets": [
{ "date": "2026-05-01T00:00:00.000Z", "count": 120, "points_spent": 25000 },
{ "date": "2026-06-01T00:00:00.000Z", "count": 120, "points_spent": 26000 }
]
}
}SDK
const { data } = await wh.rewards.aggregateRedemptions(workspaceId, {
group_by_time: "month",
exclude_cancelled: true,
});Get Redemption
GET /wallethero-api/workspace/:workspaceId/rewards/redemptions/:id
Fetches a single redemption by ID. Returns 400 (Redemption not found) if it does not exist or belongs to another workspace.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Redemption identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/REDEMPTION_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "9f3a...",
"reward_id": "8a1f...",
"client_id": "44d0...",
"points_spent": 100,
"status": "fulfilled",
"source": "self_serve",
"redemption_date": "2026-06-16T10:00:00.000Z"
}
}SDK
const { data } = await wh.rewards.getRedemption(workspaceId, redemptionId);Mark Redemption Used / Unused
POST /wallethero-api/workspace/:workspaceId/rewards/redemptions/:id/use
Toggles a redemption's "used" flag. Sets used_at (or clears it) and keeps any linked coupon in sync (used / issued). Emits a reward_used client event when marking used, and queues a wallet push.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Redemption identifier |
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
used | boolean | No | true | Set the used state. Only an explicit false marks the redemption unused; any other value (or omitted body) marks it used |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/REDEMPTION_ID/use" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "used": true }'Response (200)
{
"data": {
"redemption": {
"id": "9f3a...",
"status": "fulfilled",
"used_at": "2026-06-16T11:30:00.000Z",
"coupon_code": "COFFEE-A1B2C3",
"coupon_status": "used"
},
"coupon": { "id": "cpn_...", "code": "COFFEE-A1B2C3", "status": "used", "used_at": "2026-06-16T11:30:00.000Z" }
}
}SDK
const { data } = await wh.rewards.useRedemption(workspaceId, redemptionId, true);Cancel Redemption
POST /wallethero-api/workspace/:workspaceId/rewards/redemptions/:id/cancel
Cancels a redemption: refunds any spent points back to the client's wallet, releases any assigned coupon back to available, sets status to cancelled, emits a reward_cancelled client event, and queues a wallet push. Fails if the redemption is already cancelled.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Redemption identifier |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/REDEMPTION_ID/cancel" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"Response (200)
{
"data": {
"redemption": { "id": "9f3a...", "status": "cancelled", "points_spent": 100 },
"points_refunded": 100
}
}SDK
const { data } = await wh.rewards.cancelRedemption(workspaceId, redemptionId);Get Reward
GET /wallethero-api/workspace/:workspaceId/rewards/:id
Fetches a single reward by ID. Returns 400 (Reward not found) if it does not exist or belongs to another workspace.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Reward identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "8a1f...",
"name": "Free Coffee",
"cost_in_points": 100,
"active": true,
"purchasable": true,
"visibility_tiers": [],
"categories": ["drinks"]
}
}SDK
const { data: reward } = await wh.rewards.get(workspaceId, rewardId);Update Reward
PATCH /wallethero-api/workspace/:workspaceId/rewards/:id
Updates a reward. All fields are optional (partial update). workspace_id, id, user_created, and date_created cannot be changed. If the effective wallet_type_id is unset, purchasable is forced to false.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Reward identifier |
Request Body
Any subset of the Create Reward body fields except workspace_id. For example: name, description, code_pattern, active, cost_in_points, wallet_type_id, monetary_value, availability_start, availability_end, visibility_tiers, visibility_segments, usage_limit, usage_limit_per_client, categories, image, sort_order, purchasable, notification_config.
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": false, "cost_in_points": 150 }'Response (200)
{ "data": { "id": "8a1f...", "name": "Free Coffee", "active": false, "cost_in_points": 150 } }SDK
const { data: reward } = await wh.rewards.update(workspaceId, rewardId, { cost_in_points: 150 });
// convenience helpers (call update under the hood):
await wh.rewards.activate(workspaceId, rewardId);
await wh.rewards.deactivate(workspaceId, rewardId);Delete Reward
DELETE /wallethero-api/workspace/:workspaceId/rewards/:id
Deletes a reward. Fails with 400 if the reward has any pending redemptions.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Reward identifier |
Example Request
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{ "success": true }SDK
await wh.rewards.delete(workspaceId, rewardId);Client Available Rewards
GET /wallethero-api/workspace/:workspaceId/clients/:clientId/available-rewards
Returns all active, purchasable rewards for the workspace with a per-reward availability flag for the given client. Each entry evaluates the availability window, tier visibility, wallet balance, and global/per-client usage limits; when unavailable, a reason is provided.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
clientId | string (UUID) | Client identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/available-rewards" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"reward": { "id": "8a1f...", "name": "Free Coffee", "cost_in_points": 100 },
"available": true
},
{
"reward": { "id": "b7c2...", "name": "Premium Tote", "cost_in_points": 500 },
"available": false,
"reason": "Insufficient balance"
}
]
}SDK
const { data } = await wh.rewards.listAvailableForClient(workspaceId, clientId);Reward Stats
GET /wallethero-api/workspace/:workspaceId/rewards/:id/stats
Returns redemption statistics for a single reward: total redemptions, counts by status, and total points spent (excluding cancelled).
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Reward identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID/stats" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"total_redemptions": 132,
"pending_count": 0,
"fulfilled_count": 130,
"cancelled_count": 2,
"total_points_spent": 13000
}
}SDK
No SDK method — call the REST endpoint directly.
Reward Availability (for a client)
GET /wallethero-api/workspace/:workspaceId/rewards/:id/availability
Returns the availability evaluation for a single reward and a specific client (the client_id query parameter is required). The handler computes the full client availability list and the response includes the entry for the requested reward.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Reward identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
client_id | string (UUID) | Yes | — | Client to evaluate availability for; omitting it returns 400 (client_id is required) |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID/availability?client_id=CLIENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"reward": { "id": "8a1f...", "name": "Free Coffee", "cost_in_points": 100 },
"available": true
}
]
}SDK
const { data } = await wh.rewards.checkAvailability(workspaceId, rewardId, clientId);