Skip to content

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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Query Parameters

ParameterTypeRequiredDefaultDescription
activeboolean ("true"/"false")No— (no filter)Filter to active or inactive rewards
purchasableboolean ("true"/"false")No— (no filter)Filter to client-purchasable rewards
limitnumberNo50Max rewards to return
offsetnumberNo0Pagination offset

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards?active=true&limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier (injected as workspace_id)

Request Body

FieldTypeRequiredDefaultDescription
namestring (1–255)YesReward display name
cost_in_pointsnumber (≥ 0)YesPoints cost to redeem
descriptionstringNoReward description
code_patternstring | nullNoCoupon code template; must contain at least one {RAND:N} token with N >= 4 (e.g. COFFEE-{RAND:6})
activebooleanNotrueWhether the reward is active
wallet_type_idstring (UUID) | nullNoWallet whose points are deducted on redemption
monetary_valuenumber (≥ 0) | nullNoCash-equivalent value of the reward
availability_startstring (ISO) | nullNoEarliest date the reward can be redeemed
availability_endstring (ISO) | nullNoLatest date the reward can be redeemed
visibility_tiersstring[] (UUID)No[]Tier IDs the reward is visible to (empty = all tiers)
visibility_segmentsstring[] (UUID)No[]Segment IDs the reward is visible to
usage_limitnumber (int ≥ 0) | nullNoGlobal redemption cap across all clients
usage_limit_per_clientnumber (int ≥ 0) | nullNoPer-client redemption cap
categoriesstring[]No[]Category tags
imagestring | nullNoImage URL/reference
sort_ordernumber (int)No0Ordering weight in lists
purchasablebooleanNotrueWhether clients can self-redeem (forced false if no wallet_type_id)
notification_configobject | nullNo{ send_on_redemption?: boolean, body: string } — wallet notification on redemption (body supports template tokens)

Example Request

bash
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)

json
{ "data": { "id": "8a1f...", "name": "Free Coffee", "cost_in_points": 100, "active": true, "purchasable": true } }

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Request Body

FieldTypeRequiredDefaultDescription
reward_idstring (UUID)YesReward to redeem
client_idstring (UUID)YesClient redeeming the reward
sourceenumNoadminOne of admin, self_serve, earning_rule, mobile, campaign
metadataobjectNoArbitrary metadata stored on the redemption
skip_points_costbooleanNofalseIf true, no points are deducted and global/per-client usage limits are bypassed

Note: send_notification is honored by the service (defaults to true) but is not part of the validated redeem schema; the notification only fires when the reward's notification_config.send_on_redemption and body are set.

Example Request

bash
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)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Query Parameters

ParameterTypeRequiredDefaultDescription
reward_idstring (UUID)NoFilter by reward
client_idstring (UUID)NoFilter by client
statusenumNopending, fulfilled, cancelled, or expired
sourceenumNoadmin, self_serve, earning_rule, or mobile
from_datestring (ISO)NoRedemption date lower bound (inclusive)
to_datestring (ISO)NoRedemption date upper bound (inclusive)
limitnumber (1–500)No50Max redemptions to return
offsetnumber (≥ 0)No0Pagination offset

A workspace_id query field is also accepted by the schema but the path workspace is authoritative.

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions?status=fulfilled&limit=50" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Request Body

FieldTypeRequiredDefaultDescription
from_datestring (ISO)NoRedemption date lower bound
to_datestring (ISO)NoRedemption date upper bound
statusenumNopending, fulfilled, cancelled, or expired
exclude_cancelledbooleanNoWhen no status given, excludes cancelled redemptions
limitnumberNo5Number of top rewards (clamped to 1–50)

Example Request

bash
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)

json
{
  "data": {
    "rewards": [
      { "reward_id": "8a1f...", "reward_name": "Free Coffee", "redemption_count": 132, "points_spent": 13200 }
    ],
    "total_count": 240,
    "total_points_spent": 51000
  }
}

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Request Body

FieldTypeRequiredDefaultDescription
from_datestring (ISO)NoRedemption date lower bound
to_datestring (ISO)NoRedemption date upper bound
statusenumNopending, fulfilled, cancelled, or expired
exclude_cancelledbooleanNoWhen no status given, excludes cancelled redemptions
group_by_timeenumNoday, week, or month; when set, returns time buckets

Example Request

bash
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)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Redemption identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/redemptions/REDEMPTION_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Redemption identifier

Request Body

FieldTypeRequiredDefaultDescription
usedbooleanNotrueSet the used state. Only an explicit false marks the redemption unused; any other value (or omitted body) marks it used

Example Request

bash
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)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Redemption identifier

Example Request

bash
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)

json
{
  "data": {
    "redemption": { "id": "9f3a...", "status": "cancelled", "points_spent": 100 },
    "points_refunded": 100
  }
}

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Reward identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "8a1f...",
    "name": "Free Coffee",
    "cost_in_points": 100,
    "active": true,
    "purchasable": true,
    "visibility_tiers": [],
    "categories": ["drinks"]
  }
}

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (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

bash
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)

json
{ "data": { "id": "8a1f...", "name": "Free Coffee", "active": false, "cost_in_points": 150 } }

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Reward identifier

Example Request

bash
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{ "success": true }

SDK

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
clientIdstring (UUID)Client identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/available-rewards" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "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

typescript
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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Reward identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/rewards/REWARD_ID/stats" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "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

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Reward identifier

Query Parameters

ParameterTypeRequiredDefaultDescription
client_idstring (UUID)YesClient to evaluate availability for; omitting it returns 400 (client_id is required)

Example Request

bash
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)

json
{
  "data": [
    {
      "reward": { "id": "8a1f...", "name": "Free Coffee", "cost_in_points": 100 },
      "available": true
    }
  ]
}

SDK

typescript
const { data } = await wh.rewards.checkAvailability(workspaceId, rewardId, clientId);

WalletHero Documentation