Transactions
Transactions record purchases (and walk-in / anonymous sales) for a workspace, drive loyalty processing (points awarded/spent, applied rewards and automation rules), and back the analytics endpoints (AOV, member-vs-walkin mix, AOV by tier, aggregations).
Unlike most WalletHero resources, these endpoints are not workspace-path-scoped in the URL. The workspace is identified by a workspace_id value supplied in the query string (GET endpoints) or the request body (POST/PATCH/DELETE), and the route guard verifies the caller is a member of that workspace before the handler runs. All endpoints require a Bearer token; the token's user must belong to the named workspace.
List transactions
GET /wallethero-api/transactions
Returns transactions for a workspace, newest first, with rich filtering and pagination. Includes a meta block with total/filtered counts.
Auth: Bearer token — workspace member (workspace resolved from workspace_id query param).
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to query |
client_id | string (UUID) | No | — | Filter to a single client |
pass_id | string (UUID) | No | — | Filter to a single pass |
client_search | string | No | — | Free-text client search; matches transactions whose client matches |
from_date | string (ISO 8601) | No | — | Only transactions at/after this timestamp |
to_date | string (ISO 8601) | No | — | Only transactions at/before this timestamp |
source | string | No | — | Exact match on the transaction source |
location | string | No | — | Exact match on the transaction location |
min_amount | number | No | — | Minimum amount (inclusive) |
max_amount | number | No | — | Maximum amount (inclusive) |
external_id | string | No | — | Exact match on external id |
is_anonymous | boolean | No | — | Filter walk-in (true) vs identified (false) |
loyalty_applied | boolean | No | — | Filter rows where loyalty was applied |
tier_id | string (UUID) | No | — | Only clients currently in this tier |
limit | number | No | 50 | Page size (1–1000) |
offset | number | No | 0 | Row offset (≥ 0) |
Example Request
curl "https://api.wallethero.app/wallethero-api/transactions?workspace_id=WORKSPACE_ID&limit=20&is_anonymous=false" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "8f3c2a10-1234-4a5b-9c0d-aabbccddeeff",
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111",
"pass_id": "22222222-2222-2222-2222-222222222222",
"amount": 49.9,
"currency": "USD",
"description": "Coffee + pastry",
"external_id": "POS-1001",
"source": "idpos",
"location": "Downtown",
"transaction_timestamp": "2026-06-10T14:30:00.000Z",
"line_items": [{ "name": "Latte", "quantity": 1, "unit_price": 4.5, "total": 4.5 }],
"payment_info": { "method": "card" },
"metadata": {},
"is_anonymous": false,
"subtotal_amount": 49.9,
"discount_amount": 0,
"points_awarded": 50,
"points_spent": 0,
"applied_rewards": [],
"applied_automation_rules": [],
"loyalty_applied": true
}
],
"meta": { "total_count": 134, "filter_count": 134 }
}SDK
const { data, meta } = await wh.transactions.list({
workspace_id: workspaceId,
is_anonymous: false,
limit: 20,
});AOV breakdown
GET /wallethero-api/transactions/aov
Computes average order value (AOV) for the workspace, broken down three ways: overall, by identification (identified vs anonymous), and by whether loyalty was applied. Only transactions with amount >= 0 are counted.
Auth: Bearer token — workspace member (workspace_id query param).
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to query |
from_date | string (ISO 8601) | No | — | Only transactions at/after this timestamp |
to_date | string (ISO 8601) | No | — | Only transactions at/before this timestamp |
source | string | No | — | Exact match on the transaction source |
currency | string (3-letter) | No | — | Restrict to a single currency |
Example Request
curl "https://api.wallethero.app/wallethero-api/transactions/aov?workspace_id=WORKSPACE_ID&from_date=2026-01-01T00:00:00.000Z" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"overall": { "count": 134, "total": 6700.5, "aov": 50.0 },
"by_identification": {
"identified": { "count": 90, "total": 4950.0, "aov": 55.0 },
"anonymous": { "count": 44, "total": 1750.5, "aov": 39.78 }
},
"by_loyalty_applied": {
"applied": { "count": 90, "total": 4950.0, "aov": 55.0 },
"not_applied": { "count": 44, "total": 1750.5, "aov": 39.78 }
}
}
}SDK
const { data } = await wh.transactions.getAov({
workspace_id: workspaceId,
from_date: "2026-01-01T00:00:00.000Z",
});List transaction sources
GET /wallethero-api/transactions/sources
Returns the distinct, non-null source values present on the workspace's transactions, sorted alphabetically. Useful for building filter dropdowns.
Auth: Bearer token — workspace member (workspace_id query param).
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to query |
Example Request
curl "https://api.wallethero.app/wallethero-api/transactions/sources?workspace_id=WORKSPACE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{ "data": ["idpos", "manual", "shopify"] }SDK
const { data: sources } = await wh.transactions.getSources(workspaceId);List transaction locations
GET /wallethero-api/transactions/locations
Returns the distinct, non-null location values present on the workspace's transactions, sorted alphabetically.
Auth: Bearer token — workspace member (workspace_id query param).
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to query |
Example Request
curl "https://api.wallethero.app/wallethero-api/transactions/locations?workspace_id=WORKSPACE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{ "data": ["Downtown", "Mall", "Online"] }SDK
const { data: locations } = await wh.transactions.getLocations(workspaceId);Bulk-assign client to transactions
POST /wallethero-api/transactions/assign-client
Assigns a client to one or more anonymous/unassigned transactions. Only rows that are is_anonymous = true or have client_id IS NULL are eligible; already-identified rows are silently skipped and reported back in skipped_ids. Assigning a client emits the same downstream events as a new transaction (automation-rule processing, tier progression, webhook fan-out).
Auth: Bearer token — workspace member (workspace resolved from workspace_id in body). The target client must exist in that workspace.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
ids | string[] (UUID) | Yes | Transaction ids to assign (1–1000) |
workspace_id | string (UUID) | Yes | Workspace the transactions and client belong to |
client_id | string (UUID) | Yes | Client to assign |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/transactions/assign-client" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": ["8f3c2a10-1234-4a5b-9c0d-aabbccddeeff"],
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111"
}'Response (200)
{
"data": {
"assigned": 1,
"skipped": 0,
"skipped_ids": []
}
}SDK
const { data } = await wh.transactions.assignClient({
ids: ["8f3c2a10-1234-4a5b-9c0d-aabbccddeeff"],
workspace_id: workspaceId,
client_id: clientId,
});Update a transaction (assign client)
PATCH /wallethero-api/transactions/:id
Updates a single transaction. Currently this is restricted to assigning a client to an anonymous/unassigned transaction. The body's workspace_id is guarded; the service then verifies the transaction belongs to that workspace and that the client exists in it. Returns 400 if the transaction is already assigned to a client or not found in the workspace.
Auth: Bearer token — workspace member (workspace resolved from workspace_id in body).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Transaction id |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
workspace_id | string (UUID) | Yes | Workspace the transaction and client belong to |
client_id | string (UUID) | Yes | Client to assign |
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/transactions/8f3c2a10-1234-4a5b-9c0d-aabbccddeeff" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111"
}'Response (200)
{
"data": {
"id": "8f3c2a10-1234-4a5b-9c0d-aabbccddeeff",
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111",
"is_anonymous": false,
"amount": 49.9,
"currency": "USD",
"loyalty_applied": true
}
}SDK
const { data } = await wh.transactions.update(transactionId, {
workspace_id: workspaceId,
client_id: clientId,
});Get a transaction
GET /wallethero-api/transactions/:id
Returns a single transaction by id. The route guard loads the transaction and verifies the caller is a member of its workspace (no workspace_id query param is required for this endpoint).
Auth: Bearer token — workspace member (workspace resolved from the transaction's own workspace_id).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Transaction id |
Example Request
curl "https://api.wallethero.app/wallethero-api/transactions/8f3c2a10-1234-4a5b-9c0d-aabbccddeeff" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "8f3c2a10-1234-4a5b-9c0d-aabbccddeeff",
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111",
"pass_id": "22222222-2222-2222-2222-222222222222",
"amount": 49.9,
"currency": "USD",
"description": "Coffee + pastry",
"external_id": "POS-1001",
"source": "idpos",
"location": "Downtown",
"transaction_timestamp": "2026-06-10T14:30:00.000Z",
"line_items": [],
"payment_info": { "method": "card" },
"metadata": {},
"is_anonymous": false,
"subtotal_amount": 49.9,
"discount_amount": 0,
"points_awarded": 50,
"points_spent": 0,
"applied_rewards": [],
"applied_automation_rules": [],
"loyalty_applied": true
}
}SDK
const { data } = await wh.transactions.get(transactionId);Create a transaction
POST /wallethero-api/transactions
Creates a transaction and runs the loyalty pipeline. The transaction can be tied to a client directly (client_id), via a pass (pass_id, whose client and workspace are resolved automatically), or recorded as an anonymous walk-in (is_anonymous = true, which requires workspace_id). When workspace_id is omitted it is derived from the pass or client; in that case the workspace-membership check still runs against the resolved workspace.
loyalty_applied is computed automatically (true when any discount, points spent, points awarded, applied rewards, or applied automation rules are present). points_awarded is summed from applied_automation_rules[].points_awarded. Currency falls back to the workspace default, then "USD". If a row with the same (workspace_id, source, external_id) already exists, the existing transaction is returned (idempotent on redelivery).
Auth: Bearer token — workspace member (workspace resolved from workspace_id in body, or derived from pass_id / client_id).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Transaction total |
workspace_id | string (UUID) | Conditional | Required for anonymous transactions; otherwise derived from pass/client if omitted |
client_id | string (UUID) | Conditional | Identifies the client; one of client_id, pass_id, or is_anonymous=true is required |
pass_id | string (UUID) | Conditional | Resolves client + workspace from the pass |
is_anonymous | boolean | Conditional | true for walk-in/anonymous sales (forces client_id = null) |
currency | string (3-letter) | No | Defaults to workspace currency, then "USD" |
description | string (≤ 2000) | No | Free-text description |
external_id | string (≤ 255) | No | External system id (used for dedupe with source) |
source | string (≤ 100) | No | Origin system (e.g. idpos, manual) |
location | string (≤ 255) | No | Store / location label |
transaction_timestamp | string (ISO 8601) | No | Defaults to now |
line_items | LineItem[] | No | { name, quantity, unit_price, total, sku? } |
payment_info | object | No | { method?, provider?, reference?, ... } |
metadata | object | No | Arbitrary key/value metadata |
subtotal_amount | number | No | Pre-discount subtotal |
discount_amount | number | No | Defaults to 0 |
points_spent | number | No | Defaults to 0 |
applied_rewards | AppliedReward[] | No | { reward_redemption_id, reward_id, name, monetary_value, points_spent } |
applied_automation_rules | AppliedAutomationRule[] | No | { earning_rule_execution_id, earning_rule_id, name, points_awarded } |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/transactions" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111",
"amount": 49.9,
"currency": "USD",
"source": "manual",
"description": "Coffee + pastry"
}'Response (200)
{
"data": {
"id": "8f3c2a10-1234-4a5b-9c0d-aabbccddeeff",
"workspace_id": "WORKSPACE_ID",
"client_id": "11111111-1111-1111-1111-111111111111",
"amount": 49.9,
"currency": "USD",
"is_anonymous": false,
"discount_amount": 0,
"points_awarded": 0,
"points_spent": 0,
"applied_rewards": [],
"applied_automation_rules": [],
"loyalty_applied": false,
"transaction_timestamp": "2026-06-16T09:00:00.000Z"
}
}SDK
const { data } = await wh.transactions.create({
workspace_id: workspaceId,
client_id: clientId,
amount: 49.9,
currency: "USD",
source: "manual",
});Member-vs-walkin mix
POST /wallethero-api/transactions/mix
Returns the member (identified) vs walk-in (anonymous) split of transactions, with totals, AOV, and time-bucketed series for each slice. Buckets default to daily.
Auth: Bearer token — workspace member (workspace_id in body).
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id | string (UUID) | Yes | — | Workspace to query |
client_id | string (UUID) | No | — | Filter to a client |
client_search | string | No | — | Free-text client search filter |
from_date | string (ISO 8601) | No | — | Lower time bound |
to_date | string (ISO 8601) | No | — | Upper time bound |
source | string | No | — | Exact source match |
location | string | No | — | Exact location match |
min_amount | number | No | — | Minimum amount (inclusive) |
max_amount | number | No | — | Maximum amount (inclusive) |
is_anonymous | boolean | No | — | Restrict to anonymous/identified |
loyalty_applied | boolean | No | — | Restrict by loyalty-applied flag |
tier_id | string (UUID) | No | — | Restrict to clients currently in this tier |
group_by_time | "day" | "week" | "month" | No | "day" | Bucket size |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/transactions/mix" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "WORKSPACE_ID",
"from_date": "2026-06-01T00:00:00.000Z",
"group_by_time": "week"
}'Response (200)
{
"data": {
"member": {
"total_count": 90,
"total_sum": 4950.0,
"aov": 55.0,
"buckets": [
{ "date": "2026-06-01T00:00:00.000Z", "count": 40, "sum": 2200.0, "aov": 55.0 }
]
},
"walkin": {
"total_count": 44,
"total_sum": 1750.5,
"aov": 39.78,
"buckets": [
{ "date": "2026-06-01T00:00:00.000Z", "count": 20, "sum": 800.0, "aov": 40.0 }
]
}
}
}SDK
const { data } = await wh.transactions.getMix({
workspace_id: workspaceId,
from_date: "2026-06-01T00:00:00.000Z",
group_by_time: "week",
});AOV by tier
POST /wallethero-api/transactions/aov-by-tier
Returns member-transaction AOV grouped by the client's current tier (read live from the workspace's tier set, not snapshotted on the transaction), plus a walk-in baseline AOV and an overall member AOV. Members with no tier are reported under tier_name: "Untiered" (tier_id: null). If the workspace has no tier set, tiers is empty.
Auth: Bearer token — workspace member (workspace_id in body).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
workspace_id | string (UUID) | Yes | Workspace to query |
from_date | string (ISO 8601) | No | Lower time bound |
to_date | string (ISO 8601) | No | Upper time bound |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/transactions/aov-by-tier" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "workspace_id": "WORKSPACE_ID" }'Response (200)
{
"data": {
"tiers": [
{
"tier_id": "33333333-3333-3333-3333-333333333333",
"tier_name": "Gold",
"tier_color": "#FFD700",
"tx_count": 30,
"total_sum": 2100.0,
"aov": 70.0
},
{
"tier_id": null,
"tier_name": "Untiered",
"tier_color": null,
"tx_count": 12,
"total_sum": 480.0,
"aov": 40.0
}
],
"walkin_baseline_aov": 39.78,
"member_overall_aov": 55.0
}
}SDK
const { data } = await wh.transactions.getAovByTier({
workspace_id: workspaceId,
});Aggregate transactions
POST /wallethero-api/transactions/aggregate
Runs a single aggregation (count, sum, avg, min, or max over amount) across filtered transactions, optionally bucketed by time. count ignores amount; the others operate on the amount column.
Auth: Bearer token — workspace member (workspace_id in body).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
workspace_id | string (UUID) | Yes | Workspace to query |
function | "count" | "sum" | "avg" | "min" | "max" | Yes | Aggregation function |
client_id | string (UUID) | No | Filter to a client |
from_date | string (ISO 8601) | No | Lower time bound |
to_date | string (ISO 8601) | No | Upper time bound |
source | string | No | Exact source match |
is_anonymous | boolean | No | Restrict to anonymous/identified |
loyalty_applied | boolean | No | Restrict by loyalty-applied flag |
group_by_time | "day" | "week" | "month" | No | If set, also returns per-bucket values |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/transactions/aggregate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "WORKSPACE_ID",
"function": "sum",
"group_by_time": "month"
}'Response (200)
{
"data": {
"value": 6700.5,
"buckets": [
{ "date": "2026-05-01T00:00:00.000Z", "value": 3100.0 },
{ "date": "2026-06-01T00:00:00.000Z", "value": 3600.5 }
]
}
}SDK
const { data } = await wh.transactions.aggregate({
workspace_id: workspaceId,
function: "sum",
group_by_time: "month",
});Batch-delete transactions
DELETE /wallethero-api/transactions/batch
Permanently deletes the given transactions within a workspace. Only ids that belong to workspace_id are removed; the response reports how many rows were actually deleted.
Auth: Bearer token — workspace member (workspace_id in body).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
ids | string[] (UUID) | Yes | Transaction ids to delete (1–1000) |
workspace_id | string (UUID) | Yes | Workspace the transactions belong to |
Example Request
curl -X DELETE "https://api.wallethero.app/wallethero-api/transactions/batch" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": ["8f3c2a10-1234-4a5b-9c0d-aabbccddeeff"],
"workspace_id": "WORKSPACE_ID"
}'Response (200)
{ "data": { "deleted": 1 } }SDK
const { data } = await wh.transactions.deleteBatch(
["8f3c2a10-1234-4a5b-9c0d-aabbccddeeff"],
workspaceId,
);