Notifications
Wallet notifications let you queue and deliver push messages to a client's Apple/Google Wallet pass, and let the client read their notification feed from the loyalty portal. Notifications are inserted in a pending state and delivered by a background cron (manual sends also drain the queue immediately).
This domain has two auth models:
- Dashboard (admin) endpoints —
GET /notifications,GET /notifications/:id,POST /notifications. Bearer token of a user who is a member of the workspace (enforced bycreateWorkspaceGuard). The workspace is resolved fromworkspace_idin the query (list), from the loaded notification row (get), or fromworkspace_idin the body (send). - Public loyalty portal endpoints —
GET /public/loyalty/notifications,GET /public/loyalty/notifications/unread-count,PATCH /public/loyalty/notifications/:id/read. Authenticated with a client token issued by the loyalty portal session, passed either as atokenquery parameter or anx-client-tokenheader. The client and workspace are taken from the token, never from request input.
List notifications (admin)
GET /wallethero-api/notifications
Lists notifications for a workspace in any status, newest first. Supports filtering by client, status, channel, source type, and date range.
Auth: Bearer token — workspace member.
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to list notifications for (also used for the access check). |
client_id | string (UUID) | No | — | Restrict to a single client. |
status | string | No | — | One of pending, delivered, received, read, archived, failed. |
channel | string | No | — | Currently only wallet_notification. |
source_type | string | No | — | One of earning_rule, campaign, manual, reward_redemption, tier. |
from_date | string (ISO 8601) | No | — | Only notifications created on/after this timestamp. |
to_date | string (ISO 8601) | No | — | Only notifications created on/before this timestamp. |
limit | number | No | 50 | Page size, 1–1000. |
offset | number | No | 0 | Pagination offset. |
Example Request
curl "https://api.wallethero.app/wallethero-api/notifications?workspace_id=WORKSPACE_ID&status=delivered&limit=20" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"date_created": "2026-06-16T09:30:00.000Z",
"workspace_id": "WORKSPACE_ID",
"client_id": "CLIENT_ID",
"channel": "wallet_notification",
"title": "Points added",
"body": "You earned 50 points!",
"metadata": {},
"source_type": "manual",
"source_id": null,
"status": "delivered",
"delivered_at": "2026-06-16T09:30:05.000Z",
"read_at": null,
"scheduled_for": "2026-06-16T09:30:00.000Z",
"pass_id": "PASS_ID"
}
],
"total": 1
}SDK
const result = await wh.notifications.list({
workspace_id: workspaceId,
status: "delivered",
limit: 20,
});
// result.data: Notification[], result.total: numberGet a notification (admin)
GET /wallethero-api/notifications/:id
Returns a single notification by ID, with pass/client joins hydrated by the service. Workspace membership is enforced by pre-loading the notification row and checking the caller against its workspace_id.
Auth: Bearer token — workspace member (membership derived from the notification's workspace).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Notification identifier. |
Example Request
curl "https://api.wallethero.app/wallethero-api/notifications/NOTIFICATION_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "NOTIFICATION_ID",
"workspace_id": "WORKSPACE_ID",
"client_id": "CLIENT_ID",
"channel": "wallet_notification",
"title": "Points added",
"body": "You earned 50 points!",
"metadata": {},
"status": "read",
"read_at": "2026-06-16T10:00:00.000Z"
}
}Returns 404 with { "error": "Notification not found" } if the ID does not exist.
SDK
const { data } = await wh.notifications.get(notificationId);Send a notification (admin)
POST /wallethero-api/notifications
Queues a manual wallet notification to a client. The notification is inserted as pending; the delivery queue is drained immediately after insert (fire-and-forget) so manual sends do not wait for the next cron tick. For wallet_notification the client must have at least one active pass, otherwise the request fails with 422.
Auth: Bearer token — workspace member (membership derived from workspace_id in the body).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
workspace_id | string (UUID) | Yes | Workspace the notification belongs to. |
client_id | string (UUID) | Yes | Recipient client. |
channel | string | No | Delivery channel; defaults to wallet_notification (currently the only value). |
title | string | No | Notification title (max 255 chars). |
body | string | Yes | Notification body (min 1 char). |
metadata | object | No | Arbitrary key/value metadata. |
source_type | string | No | One of earning_rule, campaign, manual, reward_redemption, tier. Defaults to manual. |
source_id | string | No | Optional reference to the source object (max 255 chars). |
expires_at | string (ISO 8601) | No | When the notification expires. |
pass_id | string (UUID) | No | Target a specific pass. |
scheduled_for | string (ISO 8601) | No | Schedule delivery for a future time (otherwise computed from rate limits). |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/notifications" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "WORKSPACE_ID",
"client_id": "CLIENT_ID",
"title": "Points added",
"body": "You earned 50 points!"
}'Response (201)
{ "data": { "notification_id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f" } }If the client has no active passes:
{ "error": "Client has no active passes", "code": "NO_ACTIVE_PASSES" }(HTTP 422.)
SDK
const { data } = await wh.notifications.send({
workspace_id: workspaceId,
client_id: clientId,
title: "Points added",
body: "You earned 50 points!",
});
// data.notification_idList client notifications (loyalty portal)
GET /wallethero-api/public/loyalty/notifications
Returns the signed-in client's own notification feed. Only notifications that have left the queue are returned (status delivered, received, read, or archived); pending and failed are never exposed. The client and workspace come from the client token.
Auth: Client token (loyalty portal session) — pass as ?token=... or x-client-token header.
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
token | string | Yes* | — | Client session token (or send it as the x-client-token header instead). |
channel | string | No | — | Filter by channel (wallet_notification). |
source_type | string | No | — | One of earning_rule, campaign, manual, reward_redemption, tier. |
from_date | string (ISO 8601) | No | — | Only notifications created on/after this timestamp. |
to_date | string (ISO 8601) | No | — | Only notifications created on/before this timestamp. |
limit | number | No | 50 | Page size, 1–1000. |
offset | number | No | 0 | Pagination offset. |
* The token is required either as the token query parameter or the x-client-token header.
Example Request
curl "https://api.wallethero.app/wallethero-api/public/loyalty/notifications?limit=20" \
-H "x-client-token: CLIENT_TOKEN"Response (200)
{
"data": [
{
"id": "NOTIFICATION_ID",
"channel": "wallet_notification",
"title": "Points added",
"body": "You earned 50 points!",
"status": "delivered",
"delivered_at": "2026-06-16T09:30:05.000Z",
"read_at": null
}
],
"total": 1
}SDK
No SDK method — call the REST endpoint directly with the client session token.
Unread count (loyalty portal)
GET /wallethero-api/public/loyalty/notifications/unread-count
Returns the number of unread notifications (status delivered or received) for the signed-in client.
Auth: Client token (loyalty portal session) — pass as ?token=... or x-client-token header.
Example Request
curl "https://api.wallethero.app/wallethero-api/public/loyalty/notifications/unread-count" \
-H "x-client-token: CLIENT_TOKEN"Response (200)
{ "data": { "count": 3 } }SDK
No SDK method for the portal endpoint — call the REST endpoint directly with the client session token.
Note: the SDK's
wh.notifications.getUnreadCount(workspaceId, clientId)is an admin helper that queriesGET /wallethero-api/notifications?...&status=delivered&limit=0; it is not the portal endpoint above.
Mark notification as read (loyalty portal)
PATCH /wallethero-api/public/loyalty/notifications/:id/read
Marks one of the client's own notifications as read. Only notifications currently in delivered or received state can transition to read; the update is scoped to the token's client.
Auth: Client token (loyalty portal session) — pass as ?token=... or x-client-token header.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Notification identifier. |
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/public/loyalty/notifications/NOTIFICATION_ID/read" \
-H "x-client-token: CLIENT_TOKEN"Response (200)
{ "data": { "success": true } }If the notification does not exist or is not in a delivered/received state:
{ "error": "Notification not found or not in a delivered state" }(HTTP 404.)
SDK
No SDK method — call the REST endpoint directly with the client session token.