Create or update a rule
curl --request POST \
--url https://api.liddie.io/api/v1/me/auto-withdrawals \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"currency": "<string>",
"minAmount": "<string>",
"destination": "<string>",
"enabled": true,
"amountMode": "<string>",
"destinationTag": "<string>"
}
'import requests
url = "https://api.liddie.io/api/v1/me/auto-withdrawals"
payload = {
"currency": "<string>",
"minAmount": "<string>",
"destination": "<string>",
"enabled": True,
"amountMode": "<string>",
"destinationTag": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
currency: '<string>',
minAmount: '<string>',
destination: '<string>',
enabled: true,
amountMode: '<string>',
destinationTag: '<string>'
})
};
fetch('https://api.liddie.io/api/v1/me/auto-withdrawals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.liddie.io/api/v1/me/auto-withdrawals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'currency' => '<string>',
'minAmount' => '<string>',
'destination' => '<string>',
'enabled' => true,
'amountMode' => '<string>',
'destinationTag' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.liddie.io/api/v1/me/auto-withdrawals"
payload := strings.NewReader("{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.liddie.io/api/v1/me/auto-withdrawals")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.liddie.io/api/v1/me/auto-withdrawals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyAuto-withdrawals
Create or update a rule
Create or update a per-currency Liddie auto-withdrawal rule that sweeps your available balance to a whitelisted crypto address, gated by fresh MFA.
POST
/
api
/
v1
/
me
/
auto-withdrawals
Create or update a rule
curl --request POST \
--url https://api.liddie.io/api/v1/me/auto-withdrawals \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"currency": "<string>",
"minAmount": "<string>",
"destination": "<string>",
"enabled": true,
"amountMode": "<string>",
"destinationTag": "<string>"
}
'import requests
url = "https://api.liddie.io/api/v1/me/auto-withdrawals"
payload = {
"currency": "<string>",
"minAmount": "<string>",
"destination": "<string>",
"enabled": True,
"amountMode": "<string>",
"destinationTag": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
currency: '<string>',
minAmount: '<string>',
destination: '<string>',
enabled: true,
amountMode: '<string>',
destinationTag: '<string>'
})
};
fetch('https://api.liddie.io/api/v1/me/auto-withdrawals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.liddie.io/api/v1/me/auto-withdrawals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'currency' => '<string>',
'minAmount' => '<string>',
'destination' => '<string>',
'enabled' => true,
'amountMode' => '<string>',
'destinationTag' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.liddie.io/api/v1/me/auto-withdrawals"
payload := strings.NewReader("{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.liddie.io/api/v1/me/auto-withdrawals")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.liddie.io/api/v1/me/auto-withdrawals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"currency\": \"<string>\",\n \"minAmount\": \"<string>\",\n \"destination\": \"<string>\",\n \"enabled\": true,\n \"amountMode\": \"<string>\",\n \"destinationTag\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodySet up an automatic sweep: whenever your balance for
Egress (outbound money movement) is fee-free: you pay the network cost only, with no platform markup.
In addition to the rule fields, the body must include one fresh MFA factor:
The rule carries the same fields as in List rules.
currency reaches minAmount, it is sent automatically to destination.
Once a rule is enabled, funds leave your account automatically when the threshold is hit. The
destination must already be on your withdrawal whitelist for that currency — the check runs server-side, so a stolen dashboard session alone cannot point a rule at an unknown address.# The body must also include one fresh MFA factor
# (VERIFICATION_PROPS - see the Authentication guide).
curl -X POST https://api.liddie.io/api/v1/me/auto-withdrawals \
-H "Authorization: Bearer <dashboard-jwt>" \
-H "Content-Type: application/json" \
-d '{
"currency": "LTC",
"minAmount": "0.5",
"destination": "L...",
"enabled": true
}'
// Dashboard-session auth: send the dashboard JWT as a Bearer token.
// The body must also include one fresh MFA factor
// (VERIFICATION_PROPS - see the Authentication guide).
const res = await fetch('https://api.liddie.io/api/v1/me/auto-withdrawals', {
method: 'POST',
headers: {
Authorization: 'Bearer <dashboard-jwt>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
currency: 'LTC',
minAmount: '0.5',
destination: 'L...',
enabled: true,
}),
})
const body = await res.json()
Authorization
Dashboard-only (JWT) with rolemerchant_admin, merchant_member or super_admin, plus the auto-withdrawals:manage team permission. API keys are not accepted — an API key gets 401 {"error":"Invalid or expired token"}.
Because this call configures automatic money movement, the body must carry one fresh MFA factor (passkey / TOTP / emailed code — the VERIFICATION_PROPS fields; see the Authentication guide). Email codes come from Send auto-withdrawal email code. Rate limit: 10/min.
Body parameters
string
required
The currency the rule applies to — one rule per currency. 2–20 characters.
string
required
The balance threshold, as a decimal string (1–40 characters). When your balance for
currency reaches this amount, the rule fires.string
required
Where the funds go (4–256 characters). The whitelist check runs when the rule is enabled (
enabled: true) and again on every execution: an enabled rule whose destination is not whitelisted for currency is rejected with NOT_WHITELISTED. A paused rule (enabled: false) can be saved with a destination that is not currently whitelisted — it just cannot be enabled or fire until the destination is whitelisted.boolean
required
Whether the rule is active. The field is required — pass
false to create the rule in a paused state.string
"fixed" or "all" (default "all"). With "fixed" the rule withdraws exactly minAmount each time it fires; with "all" it sweeps the full available balance.string
Routing tag for tag-based currencies (XRP): a decimal string of up to 10 digits. The
(currency, destination, destinationTag) triple must be whitelisted. Omitting it on an update removes a previously stored tag.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.Whitelist the destination first. Enabling a rule (or letting it fire) is rejected server-side if
destination is not on your withdrawal whitelist for currency; a paused rule can still be saved.The withdrawal IP whitelist does not apply to auto-withdrawals — neither to managing rules on this endpoint (from any IP) nor to their scheduled execution (which has no human IP). It gates only manual withdrawals, mass payouts, and platform withdrawals. The destination wallet whitelist above is the anti-exfiltration boundary for this lifecycle.
Response
201 Created — note the status. It is 201 even when the call UPDATES an existing rule, because the endpoint is an upsert.
201 Created
{
"ok": true,
"data": {
"_id": "665f1a2b3c4d5e6f70819200",
"currency": "USDT_TRC20",
"enabled": true,
"minAmount": "100.000000",
"amountMode": "all",
"destination": "TXSf6BhvjbSVqjwmcJpBDjRm81uvvvzvZV",
"createdAt": "2026-08-12T00:00:00.000Z",
"updatedAt": "2026-08-12T00:00:00.000Z"
}
}
Errors
| Status | Body | Why |
|---|---|---|
| 400 | {"ok":false,"error":{"code":"VERIFICATION_FAILED","message":"Verification required: provide email code, OTP, or passkey"}} | The body lacks a valid fresh MFA factor. |
| 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 a missing/expired dashboard JWT. |
| 400 | {"ok":false,"error":{"code":"UNSUPPORTED_CURRENCY","message":"Unsupported currency"}} | The ticker is not in the registry. |
| 400 | {"ok":false,"error":{"code":"INVALID_MIN_AMOUNT","message":"Minimum amount must be a number"}} | minAmount does not parse as a number. |
| 400 | {"ok":false,"error":{"code":"INVALID_MIN_AMOUNT","message":"Minimum amount must be greater than zero"}} | minAmount parses but is ≤ 0. |
| 400 | {"ok":false,"error":{"code":"NO_DESTINATION","message":"Destination address is required"}} | destination is empty or all whitespace. |
| 400 | {"ok":false,"error":{"code":"NOT_WHITELISTED","message":"Destination is not a whitelisted withdrawal address for this currency"}} | The destination must already be whitelisted — the same (currency, address, tag) triple a manual withdrawal needs. |
| 400 | {"ok":false,"error":{"code":"TAG_NOT_SUPPORTED","message":"Destination tags are not supported for this currency"}} | A destinationTag was sent for a non-XRP currency. |
| 400 | {"ok":false,"error":{"code":"INVALID_DESTINATION_TAG","message":"Invalid destination tag (digits only, 0 to 4294967295, no leading zeros)"}} | The tag is not a canonical uint32. |
These fire after the MFA factor is verified, so an emailed code is already spent when you see one. Request a fresh code before retrying.
See also
- Send auto-withdrawal email code: request the emailed-code MFA factor for this call.
- List auto-withdrawal rules: confirm the rule after creating or updating it.
- Delete a rule: remove a rule you no longer want.