Loyalty Core
The loyalty core covers the program-level configuration, the points/wallet ledger operations (earn, spend, adjust, transfer, balances, ledger history), and the wallet-type definitions that describe each currency in a workspace.
All endpoints in this domain are mounted under the /wallethero-api prefix and are workspace-scoped: the caller must be a member of the workspace named in the path (enforced by createWorkspaceGuard). Authenticate with Authorization: Bearer <token> and send Content-Type: application/json on requests with a body.
In the SDK examples below, wh is a configured WalletHero client and loyalty methods live under wh.loyalty.
Loyalty Configuration
A workspace has at most one loyalty_config row. It stores the public program name, the currency label, and a free-form settings object.
Get loyalty config
GET /wallethero-api/workspace/:workspaceId/loyalty/config
Returns the workspace's loyalty configuration, or null if none exists 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/loyalty/config" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "5e1b...",
"workspace_id": "WORKSPACE_ID",
"program_name": "Acme Rewards",
"program_currency": "points",
"settings": {},
"date_created": "2026-01-10T08:00:00.000Z"
}
}When no config exists, the response is { "data": null }.
SDK
const config = await wh.loyalty.getConfig(workspaceId);Create loyalty config
POST /wallethero-api/workspace/:workspaceId/loyalty/config
Creates the loyalty config for a workspace. Fails with a validation error if a config already exists — use PATCH (or PUT) to update.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
program_name | string (max 255) | No | Public name of the loyalty program |
program_currency | string (max 50) | No | Currency label; defaults to "points" |
settings | object | No | Free-form configuration object |
workspace_idis taken from the path; do not send it in the body.
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/config" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "program_name": "Acme Rewards", "program_currency": "stars" }'Response (201)
{
"data": {
"id": "5e1b...",
"workspace_id": "WORKSPACE_ID",
"program_name": "Acme Rewards",
"program_currency": "stars",
"settings": {},
"date_created": "2026-01-10T08:00:00.000Z"
}
}SDK
const config = await wh.loyalty.createConfig(workspaceId, {
program_name: "Acme Rewards",
program_currency: "stars",
});Update loyalty config (PATCH)
PATCH /wallethero-api/workspace/:workspaceId/loyalty/config
Updates the existing loyalty config. Fails with a validation error if no config exists for the workspace.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
program_name | string (max 255), nullable | No | Public name of the loyalty program |
program_currency | string (max 50) | No | Currency label |
settings | object | No | Free-form configuration object |
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/config" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "program_name": "Acme Stars" }'Response (200)
{
"data": {
"id": "5e1b...",
"workspace_id": "WORKSPACE_ID",
"program_name": "Acme Stars",
"program_currency": "stars",
"date_updated": "2026-02-01T09:00:00.000Z"
}
}SDK
const config = await wh.loyalty.updateConfig(workspaceId, {
program_name: "Acme Stars",
});Upsert loyalty config (PUT)
PUT /wallethero-api/workspace/:workspaceId/loyalty/config
Upserts the loyalty config: if one exists it is updated (same fields/validation as PATCH, responds 200); otherwise it is created (same fields/validation as POST, responds 201).
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
When creating, accepts program_name, program_currency (default "points"), settings. When updating, accepts program_name (nullable), program_currency, settings. See POST/PATCH above for details.
Example Request
curl -X PUT "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/config" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "program_name": "Acme Rewards", "program_currency": "points" }'Response (200 if updated, 201 if created)
{
"data": {
"id": "5e1b...",
"workspace_id": "WORKSPACE_ID",
"program_name": "Acme Rewards",
"program_currency": "points"
}
}SDK
No SDK method — call the REST endpoint directly. (The SDK exposes getConfig, createConfig, and updateConfig only.)
Points & Wallets
Each client has one client_wallet per wallet type, tracking active, pending, and locked balances plus lifetime totals. Every mutation writes an immutable points_ledger entry. Earn/spend/adjust/transfer resolve the wallet type by wallet_type_code, falling back to the workspace's default wallet type when omitted. These operations also trigger pass push sync, tier re-evaluation, and loyalty.* domain events.
Get client wallet balances
GET /wallethero-api/workspace/:workspaceId/clients/:clientId/wallets
Returns the client's balances across all active wallet types (default wallet first). A wallet row is created on the fly for any active wallet type the client does not yet have.
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/wallets" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"wallet": {
"id": "w-123",
"client_id": "CLIENT_ID",
"wallet_type_id": "wt-1",
"workspace_id": "WORKSPACE_ID",
"active_balance": 150,
"pending_balance": 20,
"locked_balance": 0,
"total_earned": 200,
"total_spent": 50,
"total_expired": 0
},
"wallet_type": {
"id": "wt-1",
"code": "points",
"name": "Points",
"is_default": true,
"active": true
}
}
]
}SDK
const { data: wallets } = await wh.loyalty.getClientWallets(workspaceId, clientId);Get client points ledger
GET /wallethero-api/workspace/:workspaceId/clients/:clientId/points/ledger
Returns the paginated ledger history for one client and one wallet type. The wallet type is selected via the wallet_type_code query param, defaulting to the workspace default; if no default exists the result is empty rather than an error.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
clientId | string (UUID) | Client identifier |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
wallet_type_code | string (max 50) | No | default wallet type | Which wallet type's ledger to read |
type | enum | No | — | Filter by entry type: earn, spend, expire, cancel, transfer_in, transfer_out, adjust, lock, unlock |
status | enum | No | — | Filter by status: active, pending, locked, expired, cancelled |
from_date | string (ISO 8601) | No | — | Only entries created at/after this timestamp |
to_date | string (ISO 8601) | No | — | Only entries created at/before this timestamp |
limit | number (1–1000) | No | 50 | Max entries to return |
offset | number (≥0) | No | 0 | Pagination offset |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/clients/CLIENT_ID/points/ledger?wallet_type_code=points&limit=20" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "led-1",
"client_wallet_id": "w-123",
"client_id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"type": "earn",
"amount": 50,
"balance_after": 150,
"status": "active",
"source_type": "manual",
"created_at": "2026-02-01T09:00:00.000Z"
}
],
"meta": { "total_count": 1, "returned_count": 1, "offset": 0 }
}SDK
const ledger = await wh.loyalty.getClientLedger(workspaceId, clientId, {
type: "earn",
limit: 20,
});Note: the SDK
getClientLedgerhelper sendstype,status,limit, andoffset; to filter by a specific wallet type, call the endpoint directly withwallet_type_code.
Get workspace points ledger
GET /wallethero-api/workspace/:workspaceId/loyalty/points/ledger
Returns the paginated ledger across all clients and wallet types in the workspace, with optional filters.
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 one client |
wallet_type_code | string (max 50) | No | — | Restrict to one wallet type |
type | enum | No | — | earn, spend, expire, cancel, transfer_in, transfer_out, adjust, lock, unlock |
status | enum | No | — | active, pending, locked, expired, cancelled |
from_date | string (ISO 8601) | No | — | Only entries created at/after this timestamp |
to_date | string (ISO 8601) | No | — | Only entries created at/before this timestamp |
limit | number (1–1000) | No | 50 | Max entries |
offset | number (≥0) | No | 0 | Pagination offset |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/points/ledger?type=earn&limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "led-1",
"client_wallet_id": "w-123",
"client_id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"type": "earn",
"amount": 50,
"balance_after": 150,
"status": "active",
"created_at": "2026-02-01T09:00:00.000Z"
}
],
"meta": { "total_count": 1, "returned_count": 1, "offset": 0 }
}SDK
const ledger = await wh.loyalty.listLedgerEntries(workspaceId, {
type: "earn",
limit: 50,
});Earn points
POST /wallethero-api/workspace/:workspaceId/loyalty/points/earn
Credits points to a client's wallet. If the resolved wallet type (or override) has pending_days, the points land as pending; otherwise they are active and capped to the wallet type's max_balance. Emits a loyalty.points_earned event and triggers tier promotion and pass sync.
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 to credit |
amount | number (> 0) | Yes | Points to earn |
wallet_type_code | string (max 50) | No | Wallet type; default wallet used if omitted |
description | string (max 500) | No | Human-readable note |
source_type | string (max 50) | No | Provenance label; defaults to "manual" |
source_id | string (max 255) | No | External/source record id |
external_id | string (max 255) | No | Idempotency/external reference stored on the entry |
pending_days | number (int ≥ 0) | No | Override pending hold period (days) |
expiry_days | number (int > 0) | No | Override expiry period (days) |
metadata | object | No | Free-form metadata stored on the ledger entry |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/points/earn" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "client_id": "CLIENT_ID", "amount": 50, "description": "Welcome bonus" }'Response (201)
{
"data": {
"id": "led-1",
"client_wallet_id": "w-123",
"client_id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"type": "earn",
"amount": 50,
"balance_after": 150,
"status": "active",
"expires_at": null,
"created_at": "2026-02-01T09:00:00.000Z"
}
}SDK
const { data: entry } = await wh.loyalty.earnPoints(workspaceId, {
client_id: clientId,
amount: 50,
description: "Welcome bonus",
});Spend points
POST /wallethero-api/workspace/:workspaceId/loyalty/points/spend
Debits active points from a client's wallet using FIFO (oldest active points first). Fails if the balance is insufficient unless the wallet type allows negative balances. Emits a loyalty.points_spent event.
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 to debit |
amount | number (> 0) | Yes | Points to spend |
wallet_type_code | string (max 50) | No | Wallet type; default wallet used if omitted |
description | string (max 500) | No | Human-readable note |
source_type | string (max 50) | No | Provenance label; defaults to "manual" |
source_id | string (max 255) | No | External/source record id |
metadata | object | No | Free-form metadata |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/points/spend" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "client_id": "CLIENT_ID", "amount": 30, "description": "Reward redemption" }'Response (201)
{
"data": {
"id": "led-2",
"client_wallet_id": "w-123",
"client_id": "CLIENT_ID",
"workspace_id": "WORKSPACE_ID",
"type": "spend",
"amount": -30,
"balance_after": 120,
"status": "active",
"created_at": "2026-02-02T10:00:00.000Z"
}
}SDK
const { data: entry } = await wh.loyalty.spendPoints(workspaceId, {
client_id: clientId,
amount: 30,
description: "Reward redemption",
});Adjust points
POST /wallethero-api/workspace/:workspaceId/loyalty/points/adjust
Applies a manual balance correction. A positive amount earns points; a negative amount spends them. amount must not be 0, and description is required. Emits a loyalty.points_adjusted event (instead of the underlying earn/spend events).
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 to adjust |
amount | number (≠ 0) | Yes | Positive to add, negative to remove |
description | string (max 500) | Yes | Reason for the adjustment |
wallet_type_code | string (max 50) | No | Wallet type; default wallet used if omitted |
source_type | string (max 50) | No | Provenance label; defaults to "manual" |
source_id | string (max 255) | No | External/source record id |
metadata | object | No | Free-form metadata |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/points/adjust" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "client_id": "CLIENT_ID", "amount": -10, "description": "Correction" }'Response (201)
{
"data": {
"id": "led-3",
"client_wallet_id": "w-123",
"client_id": "CLIENT_ID",
"type": "spend",
"amount": -10,
"balance_after": 110,
"status": "active",
"created_at": "2026-02-03T11:00:00.000Z"
}
}SDK
const { data: entry } = await wh.loyalty.adjustPoints(workspaceId, {
client_id: clientId,
amount: -10,
description: "Correction",
});Transfer points
POST /wallethero-api/workspace/:workspaceId/loyalty/points/transfer
Moves active points from one client to another within the same wallet type. Spends FIFO from the source (respecting allow_negative_balance) and caps the credited amount to the recipient's max_balance. Emits paired loyalty.points_spent / loyalty.points_earned events.
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 | Source client (points debited) |
to_client_id | string (UUID) | Yes | Destination client (points credited) |
amount | number (> 0) | Yes | Points to transfer |
wallet_type_code | string (max 50) | No | Wallet type; default wallet used if omitted |
description | string (max 500) | No | Human-readable note |
metadata | object | No | Free-form metadata |
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/points/transfer" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "client_id": "FROM_CLIENT_ID", "to_client_id": "TO_CLIENT_ID", "amount": 25 }'Response (200)
{ "message": "Transfer completed successfully" }SDK
await wh.loyalty.transferPoints(workspaceId, {
client_id: fromClientId,
to_client_id: toClientId,
amount: 25,
});Note: the REST endpoint responds with a
{ "message": ... }envelope; the SDK type annotates a richerfrom/toentry shape.
Wallet Types
A wallet type defines a currency in a workspace: its code, display names, default flag, and balance rules (expiry, pending hold, max balance, negative-balance allowance, and an optional pass field to sync the balance to). Each workspace can have one default wallet type; setting a new default clears the previous one.
List wallet types
GET /wallethero-api/workspace/:workspaceId/loyalty/wallet-types
Returns all wallet types in the workspace, default first, then oldest-created first.
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/loyalty/wallet-types" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": [
{
"id": "wt-1",
"workspace_id": "WORKSPACE_ID",
"code": "points",
"name": "Points",
"unit_singular_name": "point",
"unit_plural_name": "points",
"is_default": true,
"active": true,
"allow_negative_balance": false,
"expiry_days": null,
"pending_days": null,
"max_balance": null,
"pass_sync_field": null
}
]
}SDK
const { data: walletTypes } = await wh.loyalty.listWalletTypes(workspaceId);Create wallet type
POST /wallethero-api/workspace/:workspaceId/loyalty/wallet-types
Creates a wallet type. The code must be unique within the workspace. Setting is_default: true clears the default flag from any other wallet type.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
code | string (1–50, ^[a-zA-Z_][a-zA-Z0-9_]*$) | Yes | Unique machine code for the wallet type |
name | string (1–255) | Yes | Display name |
unit_singular_name | string (max 50) | No | Singular unit label; defaults to "point" |
unit_plural_name | string (max 50) | No | Plural unit label; defaults to "points" |
is_default | boolean | No | Make this the default wallet type; defaults to false |
active | boolean | No | Whether the wallet type is usable; defaults to true |
allow_negative_balance | boolean | No | Allow spend below zero; defaults to false |
expiry_days | number (int > 0) or null | No | Days until earned points expire |
pending_days | number (int > 0) or null | No | Days earned points stay pending before activating |
max_balance | number (int > 0) or null | No | Cap on active balance |
pass_sync_field | string (max 100) or null | No | Pass custom field to sync the balance into |
settings | object | No | Free-form configuration |
workspace_idis taken from the path; do not send it in the body.
Example Request
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/wallet-types" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "code": "stars", "name": "Stars", "is_default": true, "expiry_days": 365 }'Response (201)
{
"data": {
"id": "wt-2",
"workspace_id": "WORKSPACE_ID",
"code": "stars",
"name": "Stars",
"unit_singular_name": "point",
"unit_plural_name": "points",
"is_default": true,
"active": true,
"expiry_days": 365
}
}SDK
const { data: walletType } = await wh.loyalty.createWalletType(workspaceId, {
code: "stars",
name: "Stars",
is_default: true,
expiry_days: 365,
});Get wallet type
GET /wallethero-api/workspace/:workspaceId/loyalty/wallet-types/:id
Returns a single wallet type scoped to the workspace. Responds with a validation error if the id is not found in this workspace.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Wallet type identifier |
Example Request
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/wallet-types/WALLET_TYPE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{
"data": {
"id": "wt-2",
"workspace_id": "WORKSPACE_ID",
"code": "stars",
"name": "Stars",
"is_default": true,
"active": true
}
}SDK
const { data: walletType } = await wh.loyalty.getWalletType(workspaceId, walletTypeId);Update wallet type (PUT / PATCH)
PUT /wallethero-api/workspace/:workspaceId/loyalty/wallet-types/:idPATCH /wallethero-api/workspace/:workspaceId/loyalty/wallet-types/:id
Updates a wallet type. Both methods share the same handler and accept the same partial body. Changing code to one already in use returns a validation error; setting is_default: true clears the default flag from other wallet types.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Wallet type identifier |
Request Body
All fields optional. Same field set as create (minus workspace_id):
| Field | Type | Description |
|---|---|---|
code | string (1–50, ^[a-zA-Z_][a-zA-Z0-9_]*$) | Unique machine code |
name | string (1–255) | Display name |
unit_singular_name | string (max 50) | Singular unit label |
unit_plural_name | string (max 50) | Plural unit label |
is_default | boolean | Make this the default wallet type |
active | boolean | Whether the wallet type is usable |
allow_negative_balance | boolean | Allow spend below zero |
expiry_days | number (int > 0) or null | Days until earned points expire |
pending_days | number (int > 0) or null | Days earned points stay pending |
max_balance | number (int > 0) or null | Cap on active balance |
pass_sync_field | string (max 100) or null | Pass custom field to sync the balance into |
settings | object | Free-form configuration |
Example Request
curl -X PATCH "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/wallet-types/WALLET_TYPE_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Loyalty Stars", "max_balance": 10000 }'Response (200)
{
"data": {
"id": "wt-2",
"workspace_id": "WORKSPACE_ID",
"code": "stars",
"name": "Loyalty Stars",
"max_balance": 10000,
"date_updated": "2026-02-05T12:00:00.000Z"
}
}SDK
const { data: walletType } = await wh.loyalty.updateWalletType(workspaceId, walletTypeId, {
name: "Loyalty Stars",
max_balance: 10000,
});Note: the SDK
updateWalletTypeissues aPATCH; thePUTroute exists for parity and behaves identically.
Delete wallet type
DELETE /wallethero-api/workspace/:workspaceId/loyalty/wallet-types/:id
Deletes a wallet type. Fails with a validation error if any client wallets reference it — deactivate it (active: false) instead.
Auth: Bearer token — workspace member.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
workspaceId | string (UUID) | Workspace identifier |
id | string (UUID) | Wallet type identifier |
Example Request
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/loyalty/wallet-types/WALLET_TYPE_ID" \
-H "Authorization: Bearer YOUR_TOKEN"Response (200)
{ "message": "Wallet type deleted successfully" }SDK
await wh.loyalty.deleteWalletType(workspaceId, walletTypeId);