Validate a batch (dry-run)
curl --request POST \
--url https://api.liddie.io/api/v1/mass-payouts/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"rows": [
{}
]
}
'import requests
url = "https://api.liddie.io/api/v1/mass-payouts/validate"
payload = { "rows": [{}] }
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({rows: [{}]})
};
fetch('https://api.liddie.io/api/v1/mass-payouts/validate', 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/mass-payouts/validate",
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([
'rows' => [
[
]
]
]),
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/mass-payouts/validate"
payload := strings.NewReader("{\n \"rows\": [\n {}\n ]\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/mass-payouts/validate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rows\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.liddie.io/api/v1/mass-payouts/validate")
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 \"rows\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"data.rows": [
{
"rowIndex": 123,
"currency": "<string>",
"address": "<string>",
"amount": 123,
"networkFee": {},
"netToDestination": {},
"valid": true,
"error": "<string>"
}
],
"data.totalsByCurrency": {},
"data.feeByCurrency": {},
"data.balanceByCurrency": {},
"data.canSubmit": true,
"data.overBalanceCurrencies": [
{}
]
}Mass payouts
Validate a batch (dry-run)
Dry-run a Liddie mass-payout batch before submitting — per-row validation, totals versus available balance, and a canSubmit verdict for the batch.
POST
/
api
/
v1
/
mass-payouts
/
validate
Validate a batch (dry-run)
curl --request POST \
--url https://api.liddie.io/api/v1/mass-payouts/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"rows": [
{}
]
}
'import requests
url = "https://api.liddie.io/api/v1/mass-payouts/validate"
payload = { "rows": [{}] }
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({rows: [{}]})
};
fetch('https://api.liddie.io/api/v1/mass-payouts/validate', 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/mass-payouts/validate",
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([
'rows' => [
[
]
]
]),
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/mass-payouts/validate"
payload := strings.NewReader("{\n \"rows\": [\n {}\n ]\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/mass-payouts/validate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rows\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.liddie.io/api/v1/mass-payouts/validate")
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 \"rows\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"data.rows": [
{
"rowIndex": 123,
"currency": "<string>",
"address": "<string>",
"amount": 123,
"networkFee": {},
"netToDestination": {},
"valid": true,
"error": "<string>"
}
],
"data.totalsByCurrency": {},
"data.feeByCurrency": {},
"data.balanceByCurrency": {},
"data.canSubmit": true,
"data.overBalanceCurrencies": [
{}
]
}Check a mass-payout batch before you submit it. Nothing moves, no MFA needed — you get per-row verdicts and a single
The example shows one non-whitelisted row and one invalid address.
canSubmit answer.
The validator reports per-row address validity, whitelist membership (mass-payout recipients must be whitelisted — the validator rejects a non-whitelisted address), per-currency totals and fees vs your balance, and the final canSubmit verdict.
Always run
validate before Queue a batch: it costs nothing, needs no MFA, and its canSubmit verdict tells you whether the batch as submitted would be accepted.curl -X POST https://api.liddie.io/api/v1/mass-payouts/validate \
-H "Authorization: Bearer <dashboard-jwt>" \
-H "Content-Type: application/json" \
-d '{
"rows": [
{ "currency": "LTC", "address": "L...", "amount": "0.05" },
{ "currency": "LTC", "address": "not-a-valid-address", "amount": "0.05" }
]
}'
// Dashboard-session auth: send the dashboard JWT as a Bearer token.
const res = await fetch('https://api.liddie.io/api/v1/mass-payouts/validate', {
method: 'POST',
headers: {
Authorization: 'Bearer <dashboard-jwt>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
rows: [
{ currency: 'LTC', address: 'L...', amount: '0.05' },
{ currency: 'LTC', address: 'not-a-valid-address', amount: '0.05' },
],
}),
})
const { ok, data } = await res.json()
200 OK
{
"ok": true,
"data": {
"rows": [
{ "rowIndex": 0, "currency": "LTC", "address": "L...", "amount": 0.05,
"networkFee": null, "netToDestination": null, "valid": false,
"error": "L... is not on your withdrawal whitelist for LTC" },
{ "rowIndex": 1, "currency": "LTC", "address": "not-a-valid-address", "amount": 0.05,
"networkFee": null, "netToDestination": null, "valid": false,
"error": "Invalid LTC address" }
],
"totalsByCurrency": {}, "feeByCurrency": {}, "balanceByCurrency": {},
"canSubmit": false, "overBalanceCurrencies": []
}
}
Authorization
Dashboard-only (JWT) with themass-payouts:manage permission. No MFA required — nothing moves. API keys are not accepted: an API key gets 401 {"error":"Invalid or expired token"}. Rate limit: 30/min.
Body parameters
array
required
The payout rows to check — the same rows you would submit to Queue a batch. Each row carries the recipient’s
currency, address, and amount as strings (an optional destinationTag string is supported for tag-based currencies like XRP), echoed back per-row in the response.Response fields
array
Per-row verdicts.
Show row fields
Show row fields
number
Zero-based index of the submitted row.
string
Row currency, echoed back.
string
Recipient address, echoed back.
number
Row amount as a JSON number, not a string, and not a pure echo: a valid row returns the amount rounded down to the currency’s on-chain decimals; an invalid row returns the parsed float (or
0 if it could not be parsed). For exact figures use totalsByCurrency (decimal strings).number | null
Estimated network fee for the leg;
null when the row is invalid.number | null
Amount the recipient would receive after the network fee;
null when the row is invalid.boolean
Whether this row would be accepted.
string
Human-readable reason when
valid is false (e.g. not on the whitelist, invalid address).object
Per-currency gross totals of the batch, as exact decimal strings (
{"BTC": "0.50000000"}). Parse them with a decimal library — parseFloat on an 18-decimal total loses precision, and this is the figure compared against your balance.object
Per-currency network-fee totals, as exact decimal strings.
object
Your available balance per currency, for comparison against the totals.
boolean
Single verdict: whether the batch as submitted would be accepted by Queue a batch.
array
Currencies whose batch total exceeds your available balance.
Errors
| Status | Body | Why |
|---|---|---|
| 401 | {"error":"Invalid or expired token"} | Called with an API key or a missing/expired dashboard JWT. |
See also
- Queue a batch: submit the rows once
canSubmitistrue. - List payout batches: the batches you have already queued.
- Authentication: why this endpoint takes a dashboard JWT, not an API key.