Freeze an End User
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
- Freeze exists in both s2s and the merchant panel. Unfreezing and closing are panel-only: Panel — End Users. This endpoint requires
wallet:enduser:manageorwallet:program:manageon your key.
POST /api/v1/wallet/end-users/{id}/freezeMoves the user from ACTIVE to FROZEN. Use it for a suspicious transaction, a suspected account takeover, or your own risk decision. Freezing is reversible; the balance is kept.
Request
| Header | Required | Rule |
|---|---|---|
| Signature headers | ✅ | Authentication |
Idempotency-Key | ✅ | 1–128 characters |
| Body field | Type | Required | Rule |
|---|---|---|---|
reason | string | — | Reason for freezing, up to 240 characters. |
If you have nothing to send, send an empty JSON object ({}).
curl -X POST "https://api-gateway.lunixi.com/api/v1/wallet/end-users/8d4c2a1f-3b6e-4f7a-9c5d-2e8b1a0f6c34/freeze" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: freeze-crm-88213-20261011" \
-H "X-Key-Id: …" -H "X-Date: …" -H "X-Nonce: …" -H "Digest: …" -H "X-Signature: …" \
-d '{"reason":"Customer reported a lost phone"}'{
"status": "success",
"code": "SUCCESS",
"message": "…",
"data": {
"endUser": {
"id": "8d4c2a1f-3b6e-4f7a-9c5d-2e8b1a0f6c34",
"programId": "5b9e2f3a-7c41-4d8e-a6f2-9e1b3c4d5f60",
"accountType": "PERSONAL",
"externalCustomerId": "crm-88213",
"phoneMasked": "9053******67",
"displayNameMasked": "A*** Y*****",
"status": "FROZEN",
"updatedAt": "2026-10-11T11:20:37.912Z"
}
},
"requestId": "…"
}The response returns the full endUser view; the fields are on Detail & Lookup (the example is shortened).
A frozen user
| Result | |
|---|---|
| Wallet-to-wallet transfer as sender, transfer to IBAN, withdrawal, top-up | Cannot be started — 409 WALLET_STATE_INVALID |
| Being the payer in a wallet payment | The user is treated as not found — 404 WALLET_NOT_FOUND |
| Being the recipient of a wallet-to-wallet transfer | The recipient is treated as not found — 404 WALLET_NOT_FOUND |
| Wallet lookup | Returns exists: false |
| Opening a new wallet | 409 WALLET_STATE_INVALID |
| Balance | Kept; no money moves |
| Reads (detail, balances, limit status, list) | Work |
Freezing does not reverse operations that have already started. Before deciding, check the user's open operations through Operations.
Repeated calls
- If the user is already
FROZEN, the call succeeds and returns the user unchanged. - Repeating with the same
Idempotency-Keyreturns the first response. The comparison uses the user id only; the first response is returned even ifreasondiffers. - A
CLOSEDuser cannot be frozen:409 WALLET_STATE_INVALID.
Unfreezing and closing
These two actions are not in the s2s API; your operations team performs them in the merchant panel:
- Unfreeze returns the user to
ACTIVE. - Close sets the user and all their wallets and accounts to
CLOSEDand cannot be undone. If any account has a balance, closing is rejected with409 WALLET_BALANCE_NOT_ZERO.
Details: Panel — End Users.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | reason is longer than 240 characters or the body contains an undefined field |
| 400 | — | {id} is not a UUID; Idempotency-Key missing, empty or longer than 128 characters |
| 403 | WALLET_INSUFFICIENT_SCOPE | The key lacks wallet:enduser:manage or wallet:program:manage |
| 404 | WALLET_NOT_FOUND | The user does not exist or is not yours |
| 409 | WALLET_STATE_INVALID | The user is CLOSED |
| 409 | WALLET_IDEMPOTENCY_IN_PROGRESS | The first request sent with the same key is still being processed |