curl -X POST https://api.liddie.io/api/v1/whitelist/wallets \
-H "Cookie: <dashboard session cookies>" \
-H "Content-Type: application/json" \
-d '{"currency":"LTC","address":"L...","label":"Cold wallet"}'
// Dashboard-session auth: send the session JWT as a Bearer token
const res = await fetch('https://api.liddie.io/api/v1/whitelist/wallets', {
method: 'POST',
headers: {
Authorization: `Bearer ${dashboardJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency: 'LTC',
address: 'L...',
label: 'Cold wallet',
// ...plus one fresh MFA factor (email code, OTP, or passkey assertion)
}),
})
const body = await res.json()
{"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Verification required: provide email code, OTP, or passkey"}}
Whitelist
Add wallet address
Whitelist a crypto wallet address for Liddie withdrawals and mass payouts, gated by a fresh MFA factor from the dashboard user for account security.
POST
/
api
/
v1
/
whitelist
/
wallets
curl -X POST https://api.liddie.io/api/v1/whitelist/wallets \
-H "Cookie: <dashboard session cookies>" \
-H "Content-Type: application/json" \
-d '{"currency":"LTC","address":"L...","label":"Cold wallet"}'
// Dashboard-session auth: send the session JWT as a Bearer token
const res = await fetch('https://api.liddie.io/api/v1/whitelist/wallets', {
method: 'POST',
headers: {
Authorization: `Bearer ${dashboardJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency: 'LTC',
address: 'L...',
label: 'Cold wallet',
// ...plus one fresh MFA factor (email code, OTP, or passkey assertion)
}),
})
const body = await res.json()
{"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Verification required: provide email code, OTP, or passkey"}}
Adds a wallet address to your withdrawal whitelist — the address can then receive withdrawals.
This is the security chokepoint of the egress (outbound money movement) model: adding a whitelist entry is itself MFA-gated, so an attacker with a stolen session cannot add their address and drain funds in one step.
The example request omits the MFA fields, so the server rejects it with
Every call needs a fresh MFA factor (passkey, TOTP, or emailed code) in the body. Without one, the call fails with
VERIFICATION_FAILED — status 400 when the factor is missing, 401 when it is wrong or already used, 403 when TOTP is not enrolled, 429 after too many attempts.Cookie-authenticated writes also need an
Origin header. Any non-GET request that carries the liddie_access session cookie is checked against the allowed dashboard origins; a bare cURL that sends only the cookie is rejected with 403 {"ok":false,"error":{"code":"CSRF_ORIGIN_MISMATCH","message":"Forbidden"}} before the handler runs. The snippet below is shown for shape — from a browser the dashboard sends the origin for you; from a script, prefer an API key where the endpoint accepts one.curl -X POST https://api.liddie.io/api/v1/whitelist/wallets \
-H "Cookie: <dashboard session cookies>" \
-H "Content-Type: application/json" \
-d '{"currency":"LTC","address":"L...","label":"Cold wallet"}'
// Dashboard-session auth: send the session JWT as a Bearer token
const res = await fetch('https://api.liddie.io/api/v1/whitelist/wallets', {
method: 'POST',
headers: {
Authorization: `Bearer ${dashboardJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency: 'LTC',
address: 'L...',
label: 'Cold wallet',
// ...plus one fresh MFA factor (email code, OTP, or passkey assertion)
}),
})
const body = await res.json()
{"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Verification required: provide email code, OTP, or passkey"}}
VERIFICATION_FAILED.
Authorization
Dashboard-only (JWT session). Roles:merchant_admin / merchant_member / super_admin, with team permission whitelist:manage. The body must additionally carry a fresh MFA factor (passkey / TOTP / emailed code); request an email code via Send whitelist email code. Rate limit: 10/min.
Calling with an API key fails with 401 {"error":"Invalid or expired token"} — the key is not a JWT.
Parameters
string
required
Ticker of the currency this address receives, e.g.
LTC. 2–20 characters.string
required
The wallet address to whitelist. 4–256 characters.
string
Routing tag for tag-based currencies (XRP): XRP only, and a canonical uint32: digits only,
0–4294967295, no leading zeros. "12345" ✓, "9999999999" ✗ (out of range), "0123" ✗ (leading zero). The route’s ^[0-9]{1,10}$ is only the first gate — the service rejects the rest. It becomes part of the entry’s identity — the same address with a different tag is a different entry.string
A human-readable name for the entry, e.g.
"Cold wallet". Max 64 characters.MFA fields (dashboard sessions)
MFA fields (dashboard sessions)
string
The single-use emailed code.
string
A 6-digit code from your authenticator app — an alternative to
emailCode. Backup codes are not accepted here: the field is validated against ^[0-9]{6}$ and backup codes are 16 hexadecimal characters. They work only at login (POST /2fa/validate). If you have lost your authenticator, request an emailed code instead.object
WebAuthn assertion, used together with
challengeKey.string
Accompanies
passkeyResponse.Request a fresh single-use code via Send whitelist email code right before this call, then include it in the body as the email-code verification factor — alternatively, an OTP or passkey assertion satisfies the MFA requirement.
Response
201 Created. The body echoes only the new entry’s id — nothing else about the entry is returned.
201 Created
{ "ok": true, "data": { "id": "665f1a2b3c4d5e6f70819200" } }
boolean
true on success.string
The whitelist entry id. Pass it to Remove whitelist entry to undo this.
Errors
| Status | Body | Why |
|---|---|---|
| 400 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Verification required: provide email code, OTP, or passkey"}} | No (or invalid) MFA factor in the body. |
| 401 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Invalid email code"}} | Wrong, already-used or invalid email code, OTP or passkey assertion. Same code, different status — do not treat this 401 as an expired session. |
| 403 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"TOTP is not enabled on this account"}} | A totpCode was sent by a user with no authenticator enrolled. |
| 429 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Too many incorrect codes. Request a new one."}} | Five wrong email codes — the challenge is voided; request a fresh code. |
| 429 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Too many failed attempts. Try again later."}} | TOTP lockout after repeated wrong codes. |
| 401 | {"error":"Invalid or expired token"} | Called with an API key (or an expired session) — this endpoint is dashboard-JWT only. |
| 400 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"Unsupported currency: XYZ"}} | The ticker is not in the registry. |
| 400 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"Invalid Bitcoin address for this network"}} | The address fails that currency’s format check. |
| 400 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"Destination tags are not supported for Bitcoin"}} | A destinationTag was sent for a non-XRP currency. |
| 400 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"Invalid destination tag (digits only, 0 to 4294967295, no leading zeros)"}} | The tag is not a canonical uint32. |
| 409 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"This wallet is already whitelisted"}} | The (currency, address, tag) triple is already on the list. |
| 409 | {"ok":false,"error":{"code":"WHITELIST_ADD_FAILED","message":"Maximum of 50 whitelisted wallets reached"}} | The per-merchant wallet cap. |
Every
WHITELIST_ADD_FAILED fires AFTER the MFA factor is verified, so an emailed code is already spent when you see one. Request a fresh code before retrying — resending the same body will fail on the code, not on the original problem.See also
- Send whitelist email code: get the single-use email code this endpoint accepts as an MFA factor.
- Get whitelist: list the entries currently on the whitelist.
- Request a withdrawal: the egress endpoint that requires the address you just whitelisted.