Clients
Endpoints for managing clients (customers) within a workspace: list, search, create, update, delete, anonymize (GDPR), merge, signup analytics, plus per-client passes and a client-event timeline.
All endpoints are workspace-scoped and require the caller to be a member of the workspace named in the path (enforced by createWorkspaceGuard). Authenticate with a Bearer token in the Authorization header and send Content-Type: application/json.
List clients
GET /wallethero-api/workspace/:workspaceId/clients
Returns a paginated list of clients in the workspace, with optional filtering, free-text search, and sorting. Ordered by date_created descending unless sort is provided.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
search | string (max 255) | No | — | Free-text search across name / email / phone |
status | string enum | No | — | One of active, inactive, anonymized |
source | string (max 50) | No | — | Filter by acquisition source |
email | string (max 255) | No | — | Exact email match |
from_date | string (ISO 8601) | No | — | Only clients created on/after this timestamp |
to_date | string (ISO 8601) | No | — | Only clients created on/before this timestamp |
limit | number | No | 50 | Page size (1–1000) |
offset | number | No | 0 | Pagination offset |
sort | string (max 50) | No | -date_created | Column to sort by; prefix with - for descending |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients?status=active&limit=25" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"workspace_id": "WORKSPACE_ID",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+15551234567",
"status": "active",
"marketing_consent": true,
"custom_fields": {},
"registration_date": "2026-01-10T12:00:00.000Z",
"date_created": "2026-01-10T12:00:00.000Z"
}
],
"meta": { "total": 142, "limit": 25, "offset": 0 }
}SDK
const clients = await wh.clients.list(workspaceId, {
status: "active",
limit: 25,
});Search clients
GET /wallethero-api/workspace/:workspaceId/clients/search
Free-text search across client name, email, and phone. A lighter-weight variant of list that returns a bare array (no meta).
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | Yes | — | Search query. Returns 400 if missing |
limit | number | No | 10 | Max results |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/search?q=jane&limit=5" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"status": "active"
}
]
}SDK
No SDK method — call the REST endpoint directly. (The SDK's list with a search option uses the list endpoint instead.)
Create a client
POST /wallethero-api/workspace/:workspaceId/clients
Creates a new client in the workspace. Email must be unique within the workspace — a duplicate returns a validation error. Loyalty enrollment is opt-in via loyalty_program_enabled.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string (max 255) | No | — |
last_name | string (max 255) | No | — |
email | string (email, max 255) | No | Must be unique in the workspace |
phone | string (max 50) | No | — |
birth_date | string (YYYY-MM-DD) | No | — |
marketing_consent | boolean | No | Defaults to false. When explicitly provided, the server stamps consent provenance on the client: marketing_consent_at (now), marketing_consent_source (api) and marketing_consent_text |
marketing_consent_text | string (max 2000) | No | Snapshot of the consent wording shown to the person (GDPR demonstrability). Only stored alongside a marketing_consent decision |
custom_fields | object | No | Arbitrary key/value JSON |
status | string enum | No | active (default), inactive, or anonymized |
source | string (max 50) | No | Acquisition source label |
loyalty_program_enabled | boolean | No | Enrolls the client in loyalty when true |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"marketing_consent": true
}'Response (201)
{
"data": {
"id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"workspace_id": "WORKSPACE_ID",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"marketing_consent": true,
"custom_fields": {},
"status": "active",
"registration_date": "2026-06-16T10:00:00.000Z",
"date_created": "2026-06-16T10:00:00.000Z"
}
}SDK
const client = await wh.clients.create(workspaceId, {
first_name: "Jane",
last_name: "Doe",
email: "[email protected]",
marketing_consent: true,
});Merge two clients
POST /wallethero-api/workspace/:workspaceId/clients/merge
Merges a source client into a target client: passes, events, wallets (with ledger/balance reconciliation), tiers (keeping the higher tier), automation executions, and reward redemptions are moved to the target. Missing scalar fields on the target are backfilled from the source, custom fields are merged (target wins), and the source client is deleted. Returns the resulting target client.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
source_client_id | string (UUID) | Yes | Client to merge from (deleted afterwards) |
target_client_id | string (UUID) | Yes | Client to merge into (survives) |
Both clients must exist in the workspace, and the two IDs must differ.
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/merge" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_client_id": "11111111-1111-1111-1111-111111111111",
"target_client_id": "22222222-2222-2222-2222-222222222222"
}'Response (200)
{
"data": {
"id": "22222222-2222-2222-2222-222222222222",
"workspace_id": "WORKSPACE_ID",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"status": "active"
}
}SDK
No SDK method — call the REST endpoint directly.
Signup buckets
POST /wallethero-api/workspace/:workspaceId/clients/signup-buckets
Returns counts of new clients grouped into time buckets over a date range, for dashboards. Includes the total, the latest bucket's count, and week-over-week growth.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
from_date | string (ISO 8601) | No | Start of range (filters on date_created) |
to_date | string (ISO 8601) | No | End of range |
group_by_time | string enum | No | day, week (default), or month |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/signup-buckets" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from_date": "2026-01-01T00:00:00.000Z",
"to_date": "2026-06-16T00:00:00.000Z",
"group_by_time": "week"
}'Response (200)
{
"data": {
"buckets": [
{ "date": "2026-01-05T00:00:00.000Z", "count": 12 },
{ "date": "2026-01-12T00:00:00.000Z", "count": 18 }
],
"total": 30,
"latest_bucket_count": 18,
"growth_wow_pct": 50
}
}SDK
const result = await wh.clients.getSignupBuckets(workspaceId, {
from_date: "2026-01-01T00:00:00.000Z",
to_date: "2026-06-16T00:00:00.000Z",
group_by_time: "week",
});Get a single client
GET /wallethero-api/workspace/:workspaceId/clients/:id
Fetches one client by ID. Returns a not-found error if the client does not belong to the workspace.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+15551234567",
"status": "active",
"marketing_consent": true,
"custom_fields": {},
"loyalty_program_enabled": false,
"date_created": "2026-01-10T12:00:00.000Z"
}
}SDK
const client = await wh.clients.get(workspaceId, clientId);Update a client
PATCH /wallethero-api/workspace/:workspaceId/clients/:id
Partially updates a client. Scalar fields may be set to null to clear them. Changing the email to one already used in the workspace returns a validation error. Field changes emit field_change events (source api), and toggling loyalty_program_enabled additionally re-evaluates the client's tier.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Request Body
All fields optional; only the provided fields are changed.
| Field | Type | Nullable | Description |
|---|---|---|---|
first_name | string (max 255) | Yes | — |
last_name | string (max 255) | Yes | — |
email | string (email, max 255) | Yes | Must remain unique in the workspace |
phone | string (max 50) | Yes | — |
birth_date | string (YYYY-MM-DD) | Yes | — |
marketing_consent | boolean | No | Consent provenance (marketing_consent_at, marketing_consent_source: "api", marketing_consent_text) is stamped server-side, and only on an actual value change |
marketing_consent_text | string (max 2000) | No | Snapshot of the consent wording shown to the person; stored only when marketing_consent changes |
custom_fields | object | No | Merged into existing custom fields |
status | string enum | No | active, inactive, or anonymized |
source | string (max 50) | Yes | — |
loyalty_program_enabled | boolean | No | Toggling re-evaluates the client's tier |
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "phone": "+15559998888", "loyalty_program_enabled": true }'Response (200)
{
"data": {
"id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"phone": "+15559998888",
"loyalty_program_enabled": true,
"status": "active"
}
}SDK
const client = await wh.clients.update(workspaceId, clientId, {
phone: "+15559998888",
loyalty_program_enabled: true,
});Delete a client
DELETE /wallethero-api/workspace/:workspaceId/clients/:id
Permanently deletes a client and their passes. For every provider the client has an external identity with, a durable delete_customer outbound job is enqueued so the deletion is mirrored to integrated systems (the job snapshot carries only the identifiers needed for the remote delete — no name/email). The client's events are retained for audit, but scrubbed of PII first: old_value/new_value are nulled on every field_change event except non-PII fields (status, marketing_consent), and client_name_snapshot is set to the anonymized label Deleted client instead of the real name. Orphaned events (their client_id becomes NULL) are purged by the daily retention cron after RETENTION_ORPHANED_CLIENT_EVENTS_DAYS (default 365).
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Example Request
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{ "message": "Client deleted successfully" }SDK
const result = await wh.clients.delete(workspaceId, clientId);
// => { message: "Client deleted successfully" }Anonymize a client (GDPR)
POST /wallethero-api/workspace/:workspaceId/clients/:id/anonymize
Anonymizes a client for GDPR: nulls identity fields (first_name, last_name, email, phone, birth_date), clears all custom fields, records marketing_consent: false with source anonymize, disables loyalty-program eligibility, and sets status to anonymized. Existing loyalty-portal tokens are rejected because every authenticated portal request re-checks active client status, and campaign audiences independently require status: active. Each cleared field emits a field_change event (source anonymize). The client row and its history are retained, but the event trail is scrubbed: old_value/new_value are nulled on every field_change event (including the ones just emitted by the anonymization itself) except the non-PII fields status and marketing_consent, and any client_name_snapshot is cleared.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/anonymize" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"first_name": null,
"last_name": null,
"email": null,
"phone": null,
"birth_date": null,
"custom_fields": {},
"marketing_consent": false,
"loyalty_program_enabled": false,
"status": "anonymized"
}
}SDK
const client = await wh.clients.anonymize(workspaceId, clientId);Get passes for a client
GET /wallethero-api/workspace/:workspaceId/clients/:id/passes
Returns all wallet passes belonging to the client, newest first.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/passes" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "aaaa1111-2222-3333-4444-555566667777",
"client_id": "CLIENT_ID",
"date_created": "2026-02-01T08:30:00.000Z"
}
]
}SDK
const passes = await wh.clients.getPasses(workspaceId, clientId);Create a client event
POST /wallethero-api/workspace/:workspaceId/client-events
Records a custom event on the client timeline. The workspace is derived from the client; the caller must have access to that workspace.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
client_id | string (UUID) | Yes | Client the event belongs to |
event_category | string enum | Yes | One of field_change, transaction, activity, loyalty |
event_type | string (1–50 chars) | Yes | Application-defined event type |
event_timestamp | string (ISO 8601) | No | Defaults to now |
amount | number | No | For transaction-style events |
currency | string (3 chars) | No | ISO currency code |
field_name | string (max 100) | No | For field-change events |
old_value | string | null | No | — |
new_value | string | null | No | — |
metadata | object | No | Arbitrary JSON |
external_id | string (max 255) | No | Idempotency / external reference |
source | string (max 100) | No | Origin label |
pass_id | string (UUID) | No | Associated pass |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/client-events" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client_id": "CLIENT_ID",
"event_category": "transaction",
"event_type": "purchase",
"amount": 49.99,
"currency": "USD"
}'Response (200)
{
"data": {
"id": "eeee1111-2222-3333-4444-555566667777",
"workspace_id": "WORKSPACE_ID",
"client_id": "CLIENT_ID",
"event_category": "transaction",
"event_type": "purchase",
"event_timestamp": "2026-06-16T10:00:00.000Z",
"amount": 49.99,
"currency": "USD",
"metadata": {}
}
}SDK
const event = await wh.clients.createEvent(workspaceId, {
client_id: clientId,
event_category: "transaction",
event_type: "purchase",
amount: 49.99,
currency: "USD",
});List client events
GET /wallethero-api/workspace/:workspaceId/client-events
Returns events across all clients in the workspace, newest first, with optional filtering. To fetch events for one client, use the per-client variant below.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
client_id | string (UUID) | No | — | Restrict to a single client |
event_category | string enum | No | — | field_change, transaction, activity, or loyalty |
event_types | string (CSV) | No | — | Comma-separated list of event types |
from_date | string (ISO 8601) | No | — | Filters on event_timestamp |
to_date | string (ISO 8601) | No | — | Filters on event_timestamp |
source | string (max 100) | No | — | Filter by event source |
limit | number | No | 100 | Page size (1–1000) |
offset | number | No | 0 | Pagination offset |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/client-events?event_category=transaction&limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "eeee1111-2222-3333-4444-555566667777",
"client_id": "CLIENT_ID",
"event_category": "transaction",
"event_type": "purchase",
"event_timestamp": "2026-06-16T10:00:00.000Z",
"amount": 49.99,
"currency": "USD"
}
],
"meta": { "total_count": 320, "returned_count": 50, "offset": 0 }
}SDK
No SDK method for the workspace-wide listing — use wh.clients.listEvents(workspaceId, clientId, ...) for a single client, or call this REST endpoint directly.
Get events for a specific client
GET /wallethero-api/workspace/:workspaceId/clients/:id/events
Returns the event timeline for one client, newest first. Same filters as the workspace-wide listing, scoped to the client in the path.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Client identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
event_category | string enum | No | — | field_change, transaction, activity, or loyalty |
event_types | string (CSV) | No | — | Comma-separated list of event types |
from_date | string (ISO 8601) | No | — | Filters on event_timestamp |
to_date | string (ISO 8601) | No | — | Filters on event_timestamp |
source | string (max 100) | No | — | Filter by event source |
limit | number | No | 100 | Page size (1–1000) |
offset | number | No | 0 | Pagination offset |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/events?limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "eeee1111-2222-3333-4444-555566667777",
"client_id": "CLIENT_ID",
"event_category": "loyalty",
"event_type": "tier_promoted",
"event_timestamp": "2026-06-16T10:00:00.000Z"
}
],
"meta": { "total_count": 27, "returned_count": 27, "offset": 0 }
}SDK
const events = await wh.clients.listEvents(workspaceId, clientId, {
event_category: "loyalty",
limit: 50,
});