Wallet
End users
List

List End Users

Who calls these endpoints?External API (s2s)
Base path
/api/v1/wallet
Identity
Ed25519 request signature — X-Key-Id, X-Date, X-Nonce, Digest (when there is a body), X-Signature. ±5 min clock window, single-use nonce, optional per-key IP allow-list.
Authorization
The key must carry the wallet product and the wallet permission the call requires; a call without it gets 403 WALLET_INSUFFICIENT_SCOPE. Permissions per endpoint: Key permissions.
Cross-surface
The same list exists in the merchant panel. This endpoint requires a wallet read permission (wallet:enduser:read) on your key.
GET /api/v1/wallet/end-users

Lists your end users newest first (by creation time). Phone and name are returned masked only.

Query parameters

All are optional. If you pass several filters, they are applied together.

ParameterTypeRule and meaning
programIdUUIDOnly users of this program.
accountTypestringPERSONAL or BUSINESS.
statusstringACTIVE, FROZEN or CLOSED.
corporateAccountIdUUIDOnly employees linked to this employer (corporate account). If the corporate account is not yours, 404 WALLET_NOT_FOUND is returned; the list never falls back to unfiltered.
merchantCategoryStatestringUNSET = records without a category, SET = records with a category. Personal (PERSONAL) records never have a category; to find uncategorized businesses, combine it with accountType=BUSINESS.
limitintegerPage size, 1–100. Defaults to 20.
cursorstringThe nextCursor value from the previous response. Not sent on the first page. The value is opaque; do not build or modify it.

A value outside the defined ones, or a parameter not in this list, is rejected with 400 VALIDATION_ERROR.

Example

curl "https://api-gateway.lunixi.com/api/v1/wallet/end-users?programId=5b9e2f3a-7c41-4d8e-a6f2-9e1b3c4d5f60&status=ACTIVE&limit=2" \
  -H "X-Key-Id: …" -H "X-Date: …" -H "X-Nonce: …" -H "X-Signature: …"
{
  "status": "success",
  "code": "SUCCESS",
  "message": "…",
  "data": {
    "items": [
      {
        "id": "8d4c2a1f-3b6e-4f7a-9c5d-2e8b1a0f6c34",
        "programId": "5b9e2f3a-7c41-4d8e-a6f2-9e1b3c4d5f60",
        "accountType": "PERSONAL",
        "externalCustomerId": "crm-88213",
        "corporateAccountId": null,
        "corporateOffboardedAt": null,
        "kycRef": null,
        "kycLevelCache": "LOW",
        "kycLevelRefreshedAt": "2026-10-11T10:02:44.105Z",
        "phoneMasked": "9053******67",
        "displayNameMasked": "A*** Y*****",
        "merchantCategory": null,
        "status": "ACTIVE",
        "createdAt": "2026-10-11T09:15:02.418Z",
        "updatedAt": "2026-10-11T10:02:44.105Z"
      },
      {
        "id": "3f9a6c2e-1d7b-4e5a-8c4f-6b2d9e0a1c75",
        "programId": "5b9e2f3a-7c41-4d8e-a6f2-9e1b3c4d5f60",
        "accountType": "BUSINESS",
        "externalCustomerId": "crm-77104",
        "corporateAccountId": null,
        "corporateOffboardedAt": null,
        "kycRef": null,
        "kycLevelCache": null,
        "kycLevelRefreshedAt": null,
        "phoneMasked": "9053******09",
        "displayNameMasked": "K**** K*****",
        "merchantCategory": null,
        "status": "ACTIVE",
        "createdAt": "2026-10-10T16:41:19.007Z",
        "updatedAt": "2026-10-10T16:41:19.007Z"
      }
    ],
    "nextCursor": "MjAyNi0xMC0xMCAxNjo0MToxOS4wMDcwMDArMDB8M2Y5YTZjMmU…",
    "pageInfo": { "hasMore": true, "nextCursor": "MjAyNi0xMC0xMCAxNjo0MToxOS4wMDcwMDArMDB8M2Y5YTZjMmU…", "totalCount": null }
  },
  "pageInfo": { "hasMore": true, "nextCursor": "MjAyNi0xMC0xMCAxNjo0MToxOS4wMDcwMDArMDB8M2Y5YTZjMmU…", "totalCount": null },
  "requestId": "…"
}

Each record has the same fields as endUser on Detail & Lookup.

Pagination

  • Records are in data.items.
  • When there is a next page, data.nextCursor is set and pageInfo.hasMore is true; send that value as cursor in the next request. Keep the same filters.
  • On the last page nextCursor is null and pageInfo.hasMore is false.
  • This endpoint does not return a total count (totalCount: null).
let cursor;
do {
  const qs = new URLSearchParams({ programId, limit: '100', ...(cursor && { cursor }) });
  const res = await signedGet(`/api/v1/wallet/end-users?${qs}`); // signed request
  const { data } = await res.json();
  for (const endUser of data.items) handle(endUser);
  cursor = data.nextCursor;
} while (cursor);

The list cannot be searched by phone. To reach a specific user by your own identifier use by-external; to find out whether a phone is registered in a program use wallet lookup.

Errors

HTTPCodeWhen
400VALIDATION_ERRORA parameter breaks a rule (for example limit=500, status=OPEN)
403WALLET_INSUFFICIENT_SCOPEThe key has no wallet read permission
404WALLET_NOT_FOUNDcorporateAccountId is not one of your corporate accounts