Referrals
Endpoints for configuring a workspace's referral program, browsing referral records, stats and analytics, managing individual referrals (cancel / manual complete), and fetching a single client's referral code, share link and counts. All endpoints are workspace-scoped and require Bearer-token auth from a member of the workspace.
How referrals work
- A client shares their referral code (or the
?ref=share link). When a new client — or an existing client joining the loyalty program for the first time — enrolls through a distribution/embed form with that code, apendingreferral is created. The submit response reports the attribution outcome (see below). - The referral completes when the referee meets the program's
completion_criteria:pass_install(wallet pass installed) orfirst_transaction(first purchase withamount > 0, dated after the referral was created). Completion requires the program to be enabled and the referrer to be belowmax_referrals_per_referrer. - On completion, two
referral_completedclient events are published (one per role:referrer/referee; the referrer event carriesreferrer_completed_count), which drive reward automations (referraltrigger). Events are delivered through a durable outbox — a crash cannot lose the reward. When a pending referral passespending_expiry_days, it flips toexpiredandreferral_expiredevents are published. - Referral codes are only minted for clients enrolled in the loyalty program. Disabling the program pauses pending referrals (they resume completing if re-enabled); deleting the program cancels them.
- Deleting a client anonymizes their referral history (rows are kept with a null client reference) so stats stay stable.
Attribution outcomes (distribution / embed submit)
POST /wallethero-api/distribution/submit and POST /wallethero-api/embed/distribution/:token/submit responses include referral: { outcome } whenever a referral_code was submitted. An invalid or oversized code never fails the enrollment.
| Outcome | Meaning |
|---|---|
applied | Pending referral created |
invalid_code | Code unknown in this workspace (or empty after trimming) |
program_disabled | Referral program is off |
self_referral | Code belongs to the submitter (same client or email) |
referrer_not_enrolled | Code owner is not a loyalty member |
referrer_at_cap | Code owner reached max_referrals_per_referrer |
already_attributed | The referee already has a referral |
not_eligible | Existing client already enrolled in loyalty — not a new referral |
error | Attribution failed unexpectedly (enrollment still succeeded) |
Get Referral Program
GET /wallethero-api/workspace/:workspaceId/referral-program
Returns the workspace's referral program settings (a single row per workspace). Responds 404 if no program has been configured yet.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/referral-program" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "a1b2c3d4-0000-0000-0000-000000000001",
"workspace_id": "WORKSPACE_ID",
"enabled": true,
"completion_criteria": "first_transaction",
"distribution_id": "d4c3b2a1-0000-0000-0000-000000000002",
"pending_expiry_days": 30,
"max_referrals_per_referrer": 10,
"date_created": "2026-01-10T12:00:00.000Z",
"date_updated": "2026-02-01T08:30:00.000Z"
}
}Response (404)
{
"errors": [
{ "message": "Referral program not configured", "extensions": { "code": "NOT_FOUND" } }
]
}SDK
const { data: program } = await wh.referrals.getProgram(workspaceId);Create or Update Referral Program
PUT /wallethero-api/workspace/:workspaceId/referral-program
Upserts the workspace's referral program settings. If a program already exists it is updated; otherwise a new one is created.
Disabling a program pauses its pending referrals — they stop completing but resume if the program is re-enabled.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Whether the referral program is active |
completion_criteria | "pass_install" | "first_transaction" | Yes | The event that marks a referral as completed. (enrollment was retired: instant completion at signup was abusable.) |
distribution_id | string (UUID) | null | No | Distribution landing page used to build referral share links |
pending_expiry_days | number | null | No | Days a pending referral stays valid before expiring (integer, 1–3650) |
max_referrals_per_referrer | number | null | No | Cap on completed referrals per referrer (positive integer). Enforced at attribution and completion. |
Example Request
curl -X PUT "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/referral-program" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"completion_criteria": "first_transaction",
"distribution_id": "d4c3b2a1-0000-0000-0000-000000000002",
"pending_expiry_days": 30,
"max_referrals_per_referrer": 10
}'Response (200)
{
"data": {
"id": "a1b2c3d4-0000-0000-0000-000000000001",
"workspace_id": "WORKSPACE_ID",
"enabled": true,
"completion_criteria": "first_transaction",
"distribution_id": "d4c3b2a1-0000-0000-0000-000000000002",
"pending_expiry_days": 30,
"max_referrals_per_referrer": 10
}
}SDK
const { data: program } = await wh.referrals.updateProgram(workspaceId, {
enabled: true,
completion_criteria: "first_transaction",
distribution_id: "d4c3b2a1-0000-0000-0000-000000000002",
pending_expiry_days: 30,
max_referrals_per_referrer: 10,
});Delete Referral Program
DELETE /wallethero-api/workspace/:workspaceId/referral-program
Deletes the program and cancels all its pending referrals (kill switch). Completed/expired referral history is preserved. Responds 404 when no program exists.
Auth: Bearer token — workspace member
Response (200)
{ "data": { "deleted": true, "cancelled_pending": 4 } }SDK
const { data } = await wh.referrals.deleteProgram(workspaceId);List Referrals
GET /wallethero-api/workspace/:workspaceId/referrals
Returns a paginated list of referral records for the workspace, enriched with the referrer's and referee's name and email. Supports filtering by status, by referrer/referee client, and free-text search.
Auth: Bearer token — workspace member
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
status | "pending" | "completed" | "expired" | "cancelled" | No | — | Filter by referral status |
referrer_client_id | string (UUID) | No | — | Only referrals made by this client |
referee_client_id | string (UUID) | No | — | Only referrals where this client is the referee |
search | string | No | — | Case-insensitive match on referrer/referee name, email, or referral code |
limit | number | No | 50 | Max items to return (1–100) |
offset | number | No | 0 | Number of items to skip |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/referrals?status=completed&limit=25" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "f00d0000-0000-0000-0000-000000000010",
"workspace_id": "WORKSPACE_ID",
"referrer_client_id": "c1000000-0000-0000-0000-000000000001",
"referee_client_id": "c2000000-0000-0000-0000-000000000002",
"referral_code": "K7P2QX9M",
"status": "completed",
"completion_criteria": "first_transaction",
"qualifying_event_type": "purchase",
"qualifying_event_id": "e1000000-0000-0000-0000-000000000099",
"completed_at": "2026-02-12T10:00:00.000Z",
"cancelled_at": null,
"expires_at": null,
"date_created": "2026-02-01T09:00:00.000Z",
"date_updated": "2026-02-12T10:00:00.000Z",
"referrer_first_name": "Jane",
"referrer_last_name": "Doe",
"referrer_email": "[email protected]",
"referee_first_name": "John",
"referee_last_name": "Smith",
"referee_email": "[email protected]"
}
],
"meta": { "total": 1, "limit": 25, "offset": 0 }
}referrer_client_id / referee_client_id are null when the client was deleted (referral history is anonymized, not erased).
SDK
const result = await wh.referrals.list(workspaceId, {
status: "completed",
search: "jane@",
limit: 25,
});
// result.data, result.metaReferral Stats
GET /wallethero-api/workspace/:workspaceId/referrals/stats
Returns aggregate referral counts for the workspace, grouped by status.
Auth: Bearer token — workspace member
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/referrals/stats" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": { "pending": 4, "completed": 12, "expired": 1, "cancelled": 0 }
}SDK
const { data: stats } = await wh.referrals.getStats(workspaceId);Referral Analytics
GET /wallethero-api/workspace/:workspaceId/referrals/analytics
Returns a daily created/completed timeline and the top 10 referrers.
Auth: Bearer token — workspace member
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days | number | No | 90 | Window size in days (1–365) |
Response (200)
{
"data": {
"timeline": [
{ "date": "2026-02-01", "created": 3, "completed": 1 },
{ "date": "2026-02-02", "created": 1, "completed": 2 }
],
"top_referrers": [
{
"client_id": "c1000000-0000-0000-0000-000000000001",
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"completed_count": 5,
"pending_count": 2
}
]
}
}SDK
const { data: analytics } = await wh.referrals.getAnalytics(workspaceId, 30);Cancel a Referral
POST /wallethero-api/workspace/:workspaceId/referrals/:referralId/cancel
Voids one pending referral. Responds 404 for an unknown referral and 409 when the referral is not pending.
Auth: Bearer token — workspace member
Response (200)
{ "data": { "id": "f00d0000-0000-0000-0000-000000000010", "status": "cancelled", "cancelled_at": "2026-07-06T12:00:00.000Z" } }SDK
const { data: referral } = await wh.referrals.cancel(workspaceId, referralId);Cancel All Pending Referrals
POST /wallethero-api/workspace/:workspaceId/referrals/cancel-pending
Voids every pending referral in the workspace (kill switch — e.g. after detecting abuse). Returns the number cancelled.
Auth: Bearer token — workspace member
Response (200)
{ "data": { "cancelled": 7 } }SDK
const { data } = await wh.referrals.cancelAllPending(workspaceId);Manually Complete a Referral
POST /wallethero-api/workspace/:workspaceId/referrals/:referralId/complete
Admin override: completes a pending referral regardless of program state, referrer cap, or expiry, and publishes the normal referral_completed events (so configured reward automations fire). qualifying_event_type is recorded as "manual". Responds 404 for an unknown referral and 409 when it is not pending.
Auth: Bearer token — workspace member
Response (200)
{ "data": { "id": "f00d0000-0000-0000-0000-000000000010", "status": "completed", "qualifying_event_type": "manual" } }SDK
const { data: referral } = await wh.referrals.complete(workspaceId, referralId);Get Client Referral Info
GET /wallethero-api/workspace/:workspaceId/clients/:clientId/referral
Returns one client's referral code, the share link for distributing it, and the client's referral stats. Used to power the per-client referral panel in the dashboard.
A missing code is only minted when the program is enabled and the client is enrolled in the loyalty program — browsing client pages does not create codes as a side effect. Already-assigned codes are always returned.
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/referral" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"code": "K7P2QX9M",
"share_url": "https://app.wallethero.com/distribution/public/loyalty-signup?ref=K7P2QX9M",
"stats": { "pending": 2, "completed": 5, "expired": 0, "cancelled": 0 },
"program_enabled": true
}
}code is null when the client has no code yet and it cannot be minted (program disabled/missing, or client not loyalty-enrolled). share_url is null when there is no code or the referral program has no distribution_id configured (or the distribution has no public page address).
SDK
const { data: info } = await wh.referrals.getClientReferral(workspaceId, clientId);