Mass payouts

Pay your own recipients in batches, from your own payout wallet: salaries and contractors, tournament prizes and affiliate commissions, withdrawals from a trading platform or a marketplace. You upload a list (or send it over the API), payitgo checks and prices every row, you approve the batch, and payitgo sends each payment as soon as the wallet holds enough.

Coins 14 coins on 7 networks: USDT on Tron, BNB Smart Chain and Ethereum; USDC on BNB Smart Chain and Ethereum; DAI; ETH on Ethereum and BNB Smart Chain; TRX, BNB, BTC, LTC, DOGE and XRP (networks and coins)
Where the money comes from Your payout wallet: one address per network, yours alone, that you top up
What it costs 0.5% of each payment, never less than $0.50, plus its network fee (Fees)
How you send payments Each payout request as it comes in, sent together when you choose (Payout requests); or whole batches from the dashboard (rows typed or pasted, or a CSV file) or the API (The API)
No address? A claim link: the recipient chooses the coin and their wallet
Other coins Conversion: keep one coin in the wallet and pay in any payout coin
Payroll Schedules: the same batch every week, two weeks or month
Safeguards Approvals, limits per payment, per batch and per day, a wait for new recipients, sanctions screening (Approvals and limits)

Test mode has it all, on paper: a test key, or the dashboard's test mode, tops up a test wallet with a click and sends nothing on chain (In test mode).

How it works

  1. Make a batch, or let your payout requests make one. Each payment names a coin, an address, an amount and, if you like, your own reference for it and the recipient's name, email and a note.
  2. payitgo checks and prices it. Every row is checked (the coin, the amount, the address down to its checksum, the sanctions list, your limits) and priced. A row with an error is left out; the rest are ready. The batch is quoted: its prices hold for an hour.
  3. Submit it. With approvals turned on, it waits for them; otherwise it goes straight to funding.
  4. It is funded from your payout wallet. When the wallet holds what the batch needs, coin by coin, its money is set aside and it starts. When it does not, the batch waits and says exactly how much to send, and where. It starts by itself once the money is there.
  5. payitgo sends it. The batch's fees go to payitgo first, then its payments, up to 50 in each transfer (200 in one Bitcoin, Litecoin or Dogecoin transaction). Each payment gets its transaction id as it goes, and a webhook.

Payout requests

The easiest way in. You don't have to build batches: your system sends each payout request the moment it comes in (a trader withdrawing from your platform, a contractor's invoice approved, an employee's pay run, a prize won) with POST /v1/payouts. payitgo checks and prices it at once, answers with what it costs, and keeps it in your list of payout requests. When you choose (every hour, at the end of the day, or when someone presses Send in the dashboard), send the list: its valid requests become a batch, priced again at today's fees and submitted, then approved and funded like any batch. Or let the list go by itself after a few minutes (auto-send).

A trading platform's withdrawals, start to finish:

  1. A client asks to withdraw 250 USDT to their Tron wallet. Your server sends it to payitgo with your own id for the request as reference:

    curl -s https://api.payitgo.co/v1/payouts \
      -H "Authorization: Bearer $PAYITGO_KEY" \
      -H 'Content-Type: application/json' \
      -d '{"reference": "wd-88231", "asset": "USDT_TRC20", "address": "TR7mNkucnhLZoJQLLJ6DR88jpLQufWxUJi", "amount": "250", "name": "Ana Silva", "email": "[email protected]"}'
    

    201 Created: the payment, ready with its fee. One that cannot be paid comes back invalid with its errors (a wrong network, a sanctioned address, under the minimum): tell your client at once.

    {
      "item": {
        "id": "5c7e9a1b3d5f7a9c1e3b5d7f",
        "merchant_id": "3f8a1c27b94e05d6a2c71e90",
        "livemode": true,
        "batch_id": "0d2f4a6c8e1b3d5f7a9c2e4b",
        "reference": "wd-88231",
        "row": 14,
        "recipient": { "name": "Ana Silva", "email": "[email protected]" },
        "asset": "USDT_TRC20",
        "ticker": "USDT",
        "icon_url": "https://api.payitgo.co/coins/USDT_TRC20.svg",
        "chain": "tron",
        "address": "TR7mNkucnhLZoJQLLJ6DR88jpLQufWxUJi",
        "amount": "250",
        "send_amount": "250",
        "fee": "2.852417",
        "debit": "252.852417",
        "usd_rate": "1",
        "amount_usd": "250",
        "status": "ready",
        "errors": [],
        "warnings": [],
        "created_at": "2026-10-20T13:41:07.52Z",
        "updated_at": "2026-10-20T13:41:07.52Z"
      }
    }
    
  2. More requests come in the same way. At any time, read the list and what sending it takes from the wallet:

    curl -s https://api.payitgo.co/v1/payouts/inbox -H "Authorization: Bearer $PAYITGO_KEY"
    
  3. Send it: all its valid requests, or only the ones you pick (ids):

    curl -s -X POST https://api.payitgo.co/v1/payouts/send -H "Authorization: Bearer $PAYITGO_KEY"
    

    The answer is the new batch, already submitted: waiting for its approvals, waiting for a top-up (waiting.shortfall says what to send where), or being sent.

  4. Each request then tells you what became of it, by webhook, with your reference:

    • payout_item.processing: it left the list for a batch: show the withdrawal as processing;
    • payout_item.sent, with the tx_id: mark the withdrawal paid;
    • payout_item.failed: it was not paid, its money is still in your wallet;
    • payout_item.canceled: it will never be paid, and cancel_reason says why (removed from the list, rejected by an approver of your team, its batch canceled, no longer payable): give your client their money back.

    Every request ends sent, failed or canceled, and you hear it, whoever acted on it: your system, a team member in the dashboard, or payitgo. See Staying in sync. Payroll works the same way: send each employee's pay as it is approved, and the list at the end of the day.

A request's reference makes it safe to send again: the same reference answers 200 OK with the payment that has it, wherever it is now. Once a request is canceled, failed or expired, its reference is free again: to pay it after all, send a new request (it may reuse the reference). A canceled payment is never brought back.

Take a payout request

POST /v1/payouts · a key with payouts on

The body is one payment: asset, address and amount, and optionally reference, name, email, note, tag, claim and claim_assets (as in Create a batch), and webhook_url: where this request's events go instead of your account's webhook URL (https; it stays with the request when it is sent). 201 Created: {"item": payment}, in your list (its batch_id is the list's), ready with its fee and debit, or invalid with its errors. An invalid request stays in the list, never sent, until you remove it. 200 OK with the payment its reference already names. New requests follow your payout settings' fee_paid_by and convert as they are when the request comes in; when sent, each is priced again and follows the conversion setting as it is then (converted again, or no longer converted, to match).

Status Message Cause
400 invalid request body Not JSON, or a field of the wrong type
400 webhook_url: url must use https · webhook_url: url host does not resolve · webhook_url: url must point at a public host · webhook_url: url must point at your own server, not payitgo's An unusable webhook_url
403 this api key cannot make payouts: turn payouts on for it in the dashboard (payout keys)
409 your list of payout requests is full: send or cancel some first 100,000 requests waiting

Read the list

GET /v1/payouts/inbox?status=&limit=&offset= · any key

{
  "inbox": {
    "id": "0d2f4a6c8e1b3d5f7a9c2e4b",
    "merchant_id": "3f8a1c27b94e05d6a2c71e90",
    "livemode": true,
    "name": "Payout requests",
    "source": "requests",
    "inbox": true,
    "open": true,
    "created_by": "requests",
    "by_api_key": false,
    "fee_paid_by": "merchant",
    "convert": "same_network",
    "status": "quoted",
    "totals": [
      { "asset": "USDT_TRC20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_TRC20.svg", "chain": "tron", "network": "Tron (TRC-20)", "items": 1, "amount": "250", "send_amount": "250", "fee": "2.852417", "debit": "252.852417", "debit_usd": "252.85", "saved_usd": "0" },
      { "asset": "USDT_BEP20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg", "chain": "bsc", "network": "BNB Smart Chain (BEP-20)", "items": 1, "amount": "75", "send_amount": "75", "fee": "0.51183421", "debit": "75.51183421", "debit_usd": "75.51", "saved_usd": "0" }
    ],
    "counts": { "total": 3, "invalid": 1, "ready": 2, "cooling": 0, "queued": 0, "sending": 0, "sent": 0, "failed": 0, "canceled": 0, "claimable": 0, "expired": 0 },
    "total_usd": "328.36",
    "approvals_needed": 0,
    "approvals": [],
    "members": {},
    "created_at": "2026-10-18T08:02:11.4Z",
    "updated_at": "2026-10-20T13:52:40.08Z"
  },
  "coverage": [
    { "asset": "USDT_TRC20", "ticker": "USDT", "network": "Tron (TRC-20)", "needed": "252.852417", "spendable": "100", "top_up": "152.852417", "address": "TMkq7dZ3vN8xR2pB6cH9sJ4fL1wY5tGaQe" },
    { "asset": "USDT_BEP20", "ticker": "USDT", "network": "BNB Smart Chain (BEP-20)", "needed": "75.51183421", "spendable": "500", "top_up": "0", "address": "0x8F3a6B2c9D1e4F7a0B5c8D2e6F9a1B4c7D0e3F6a" }
  ],
  "items": [ { "id": "5c7e9a1b3d5f7a9c1e3b5d7f", "reference": "wd-88231", "status": "ready", "…": "…" } ],
  "total": 3,
  "limit": 50,
  "offset": 0
}
Field Description
inbox The list itself, a batch with inbox and open true: the totals and counts of the requests waiting in it (valid ones in totals). It is never submitted, and never listed among your batches; batch routes on it answer 409 this is your list of payout requests: …
coverage Per coin of the valid requests: needed (what sending them takes from the wallet, their debits), spendable (what the wallet can spend now), top_up (what to send to address first; "0" when it is covered), with ticker and network
items The requests waiting, in the order they came (status filters: ready, invalid, or both, comma-separated); total, limit (1 to 200, 50 by default) and offset page them

Send the list

POST /v1/payouts/send · a key with payouts on

No body sends every valid request (up to 10,000 a call: the rest wait for the next); {"ids": ["5c7e9a1b3d5f7a9c1e3b5d7f", …]} sends only those. They move into a new batch (source: "requests", inbox: true, named "Payout requests of 20 Oct 2026 14:00 UTC"), are priced again at today's fees, and the batch is submitted as the key: then approvals, funding and the top-up it may need, as for any batch made with an API key. Up to 100 requests are priced while the call waits; more come back validating, and the batch is submitted by itself once priced. Invalid requests, and the ones you did not pick, stay in the list. Each request sent raises payout_item.processing, with its new batch_id.

A request no longer valid when priced again (prices moved under the minimum, an address added to the sanctions list, your limit per payment lowered) comes back to the list with its errors, and raises payout_item.returned; the others go on. It stays in the list, never sent and never priced again, until you remove it.

200 OK: {"batch": batch}. Should submitting it fail (none of its requests is valid any more at today's prices, or it is over your limit per batch), its requests go back to the list, each saying why (payout_item.returned), and that batch is canceled (payout_batch.canceled, canceled_by: "system"); the answer is the error. For a send priced in the background, your team gets a "Payout requests not sent" notification instead.

Status Message Cause
400 no payout request to send · no valid payout request to send The list is empty, or has only invalid requests
400 payout request 5c7e9a1b3d5f7a9c1e3b5d7f is not a valid request waiting in your list An id not ready in the list
400 none of the requests is valid any more: they are back in your list, each saying why Priced again, none can be paid
400 this batch takes $328.36, over your limit of $250.00 per batch Submitting it was refused: the requests are back in the list
403 this api key cannot make payouts: …

Remove a request

DELETE /v1/payouts/{id} · a key with payouts on

A request still in the list is removed: 200 OK with the payment, "status": "canceled", "cancel_reason": "removed". Its reference is free again (it moves to released_reference), it no longer has a batch_id, and its record stays: GET /v1/payouts/{id} keeps answering it. payout_item.canceled is sent.

One already sent but not on its way yet is canceled like any payment ("cancel_reason": "canceled"): funded (queued, cooling, sending), its money stays in the wallet and its fee comes back; not funded yet, its batch is totalled again (approvals already given stand), and canceled when nothing is left in it. A request sent at the very moment you remove it is canceled where it went, never reported removed while it is paid.

Removing a payment canceled already answers it again, unchanged: safe to repeat. 404 not found for another mode's payment; 409 the payment is sent (or failed, expired) once it is done.

To pay a removed request after all, send it again as a new request: it may reuse the same reference.

Remove several requests

POST /v1/payouts/remove · a key with payouts on

{"ids": ["5c7e9a1b3d5f7a9c1e3b5d7f", "7b1d3f5a9c2e4b6d8f0a1c3e"]}, 1 to 1,000 ids (one given twice counts once). Each request still in the list is removed as above, with its own payout_item.canceled; the list is totalled again once. 200 OK with a result per id, in the order given:

{
  "results": [
    { "id": "5c7e9a1b3d5f7a9c1e3b5d7f", "item": { "id": "5c7e9a1b3d5f7a9c1e3b5d7f", "status": "canceled", "cancel_reason": "removed", "released_reference": "wd-88231", "…": "…" } },
    { "id": "7b1d3f5a9c2e4b6d8f0a1c3e", "error": { "status": 409, "message": "not in your list of payout requests (sent already): cancel it with DELETE /v1/payouts/7b1d3f5a9c2e4b6d8f0a1c3e" } }
  ]
}
Result When
item Removed now, or removed already (nothing changes, no second event)
error 404 not found No such payment in this mode
error 409 not in your list of payout requests (sent already): … Sent from the list already: this call never cancels a request that left the list. Cancel it with DELETE /v1/payouts/{id}
error 409 the payment is sent (or failed, expired, canceled) It ended
Status Message Cause
400 remove between 1 and 1000 payout requests at a time No ids, or too many
400 invalid request body
403 this api key cannot make payouts: …

Auto-send

With auto_send_minutes in your payout settings (1 to 1440; 0, the default, sends by hand), the list sends itself once its oldest valid request has waited that long: a withdrawal requested at 14:00 with 30 minutes goes at 14:30, with everything valid that came in since. The batch is made and submitted by auto-send (its created_by), with the approval rules of a batch made with an API key: approvals unless api_auto_submit lets it skip them (and it never skips them for a new recipient). Auto-send waits while payitgo has paused your payouts. Turning auto-send on, or shortening it, loosens your rules: it takes effect 24 hours later.

The payout wallet

Your payout wallet is a set of addresses payitgo made for your account alone: one per network (Tron, BNB Smart Chain, Ethereum, Bitcoin, Litecoin, Dogecoin and the XRP Ledger), the same for every coin of that network. It is made the first time it is asked for (opening the payout wallet page, or GET /v1/payout-wallets), and it never changes. It holds only your money, kept apart from every other account's: top it up with what your batches need.

payitgo runs the wallet for you and signs its transfers, which only ever pay your batches' recipients, your batches' fees, and withdrawals back to your own payout addresses.

Networks and coins

Network chain Coins Network fees paid with
Tron tron USDT_TRC20, TRX payitgo's TRX on the wallet
BNB Smart Chain bsc USDT_BEP20, USDC_BEP20, ETH_BEP20, BNB payitgo's BNB on the wallet
Ethereum ethereum USDT_ERC20, USDC_ERC20, DAI_ERC20, ETH payitgo's ETH on the wallet
Bitcoin bitcoin BTC The wallet's own BTC
Litecoin litecoin LTC The wallet's own LTC
Dogecoin dogecoin DOGE The wallet's own DOGE
XRP Ledger xrp XRP The wallet's own XRP

On Tron, BNB Smart Chain and Ethereum, payitgo pays each transfer's network fee with its own coin, kept on the wallet for that, and charges it in each payment's fee: your TRX, BNB or ETH are never spent on fees. On Bitcoin, Litecoin, Dogecoin and the XRP Ledger, the wallet's own coin pays the network: each payment's network fee stays in the wallet, set aside (reserved) until its transaction spends it, and only the platform fee is sent to payitgo.

Send only these coins to their network's address. Another token sent there is not counted automatically: contact support and payitgo sorts it out with you.

Balances

GET /v1/payout-wallets (any key) or GET /merchant/payout-wallets?mode=test (any role) shows each address, and what it holds of each coin:

curl -s https://api.payitgo.co/v1/payout-wallets -H "Authorization: Bearer $PAYITGO_KEY"

200 OK:

{
  "wallets": [
    {
      "chain": "tron",
      "network": "Tron",
      "address": "TMkq7dZ3vN8xR2pB6cH9sJ4fL1wY5tGaQe",
      "explorer_url": "https://tronscan.org/#/address/TMkq7dZ3vN8xR2pB6cH9sJ4fL1wY5tGaQe",
      "livemode": true,
      "assets": [
        { "asset": "USDT_TRC20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_TRC20.svg", "balance": "500", "reserved": "122.202417", "spendable": "377.797583", "needed": "0" },
        { "asset": "TRX", "ticker": "TRX", "icon_url": "https://api.payitgo.co/coins/TRX.svg", "balance": "0", "reserved": "0", "spendable": "0", "needed": "0" }
      ]
    },
    {
      "chain": "bsc",
      "network": "BNB Smart Chain",
      "address": "0x8F3a6B2c9D1e4F7a0B5c8D2e6F9a1B4c7D0e3F6a",
      "explorer_url": "https://bscscan.com/address/0x8F3a6B2c9D1e4F7a0B5c8D2e6F9a1B4c7D0e3F6a",
      "livemode": true,
      "assets": [
        { "asset": "USDT_BEP20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg", "balance": "100", "reserved": "0", "spendable": "100", "needed": "151.26183421" },
        { "asset": "USDC_BEP20", "ticker": "USDC", "icon_url": "https://api.payitgo.co/coins/USDC_BEP20.svg", "balance": "0", "reserved": "0", "spendable": "0", "needed": "0" },
        { "asset": "ETH_BEP20", "ticker": "ETH", "icon_url": "https://api.payitgo.co/coins/ETH_BEP20.svg", "balance": "0", "reserved": "0", "spendable": "0", "needed": "0" },
        { "asset": "BNB", "ticker": "BNB", "icon_url": "https://api.payitgo.co/coins/BNB.svg", "balance": "0", "reserved": "0", "spendable": "0", "needed": "0" }
      ]
    },
    { "chain": "ethereum", "network": "Ethereum", "…": "…" }
  ]
}

The other networks follow in the same shape: Ethereum, Bitcoin, Litecoin, Dogecoin, the XRP Ledger.

The wallet object

Field Type Description
chain string tron, bsc, ethereum, bitcoin, litecoin, dogecoin or xrp
network string The network for people: Tron, BNB Smart Chain, Ethereum, Bitcoin, Litecoin, Dogecoin, XRP Ledger
address string Where to send that network's coins. Show it with a QR code
explorer_url string, optional The address on the network's block explorer. Live only
livemode boolean false for the test wallet
assets array What it holds of each payout coin of that network (below)
error string, optional Why its balances could not be read just now (the figures are then 0): try again in a moment

Each asset:

Field Type Description
asset string The coin: one of the payout coins of that network
ticker, icon_url string What people call it ("USDT") and its icon, an SVG image (Currencies)
balance decimal string What the address holds, read from the chain (at most 20 seconds old). For TRX, BNB and ETH, payitgo's own coin kept there for network fees is left out; for XRP, the ledger's reserve; for BTC, LTC and DOGE it counts confirmed coins and the wallet's own change. In test mode, the balance on paper
reserved decimal string Promised already: payments funded and not sent yet (with their network fee on Bitcoin, Litecoin, Dogecoin and XRP), claim links waiting (their worst-case debit), fees not sent yet, withdrawals not sent yet
spendable decimal string balance less reserved: what a new batch or a withdrawal can use
needed decimal string What your batches waiting for funds need beyond spendable: the top-up to send now. "0" when nothing waits
withdrawal_fee decimal string What withdrawing it costs right now, taken from the amount: a withdrawal must be more than it

A batch waiting for funds also says what it lacks, coin by coin, with the address to send it to (its waiting.shortfall).

Unused funds

Top the wallet up as your batches need it, and withdraw whatever you like, whenever you like. Every 6 hours payitgo looks at each live payout wallet; one holding more than $25,000 of spendable funds (payitgo's threshold today) for a week tells your team, at most once a week, with a notification in the payouts category, so unused funds never go unnoticed: "Payout wallet holding unused funds: Your payout wallet has held about $31000 unused for a week. If your next payouts won't need it all, you can withdraw the rest to your own payout address at any time." It is in your audit log too (payout_wallet.idle).

Withdrawals

What the wallet holds can be sent back to one of your payout addresses for that coin (one past its 24-hour wait): from the dashboard's payout wallet page, by the owner, with their password and their two-factor code. There is no API route for it: an API key can never send the wallet's money anywhere but to a batch's recipients.

POST /merchant/payout-wallets/{asset}/withdraw?mode=test · owner

Field Type Required Description
amount decimal string yes, unless all How much of the coin leaves the wallet
all boolean no true withdraws everything spendable
address_id string yes One of your payout addresses that receives that coin
password string yes The owner's password
code string when they have two-factor sign-in A code from their authenticator app (or a recovery code)
curl -s https://api.payitgo.co/merchant/payout-wallets/USDT_TRC20/withdraw \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"all": true, "address_id": "3e7a1c9f5b2d4e8a6c0f2b1d", "password": "a-long-password", "code": "492071"}'

201 Created:

{
  "withdrawal": {
    "id": "1d3f5a7c9e2b4d6f8a0c2e4b",
    "merchant_id": "3f8a1c27b94e05d6a2c71e90",
    "livemode": true,
    "asset": "USDT_TRC20",
    "chain": "tron",
    "amount": "377.797583",
    "fee": "3.71",
    "send_amount": "374.087583",
    "address_id": "3e7a1c9f5b2d4e8a6c0f2b1d",
    "address": "TWd4Kp9mR2xH7sN3vB6cJ8fL5qY1tZgEaU",
    "status": "requested",
    "requested_by": "9b2e4c71d8a05f3e6c1a7b24",
    "members": { "9b2e4c71d8a05f3e6c1a7b24": "[email protected]" },
    "created_at": "2026-10-20T11:02:15.33Z",
    "updated_at": "2026-10-20T11:02:15.33Z"
  }
}

A withdrawal pays the network fee of two transfers (yours, and the one taking its fee to payitgo), in the coin, out of amount (the coin's withdrawal_fee in the wallet says how much right now): send_amount is what reaches your address. Its status goes requested → sending → sent (with tx_id, and in live mode explorer_url, the transaction on the block explorer), or failed, which leaves the money in the wallet. When it is sent, a payout_wallet.withdrawn webhook and a "Payout wallet withdrawal sent" notification follow. GET /merchant/payout-wallets/withdrawals?mode=test lists the latest 50. While payitgo has paused payouts on your account, live withdrawals are refused too.

Status Message Cause
400 payouts in SHIB_ERC20 are not available yet Not a payout coin
400 address_id is not one of your payout addresses · that payout address receives USDT_BEP20, not USDT_TRC20 The wrong address
400 that payout address is used from 2026-10-21T09:00:29Z The address is still in its 24-hour wait
400 nothing to withdraw · the wallet has 12.5 USDT_TRC20 to spare Nothing spendable, or more than spendable
400 withdrawing costs 3.71 USDT_TRC20: withdraw more than that The fees would take it all
400 two-factor code is incorrect or missing · turn on two-factor sign-in first: this takes a code from it The code, or two-factor sign-in that your payout settings require
403 password is incorrect
403 insufficient permissions Not the owner
409 payouts are paused on this account: contact support payitgo paused your payouts (live mode)

A batch's life

validating ──▶ quoted ──▶ pending_approval ──▶ awaiting_funds ──▶ processing ──▶ completed
                 │  ▲            │                   │
                 │  └ add, remove, new quote         │
                 ▼               ▼                   ▼
             canceled         rejected            canceled (by you, or by payitgo
                                                  when nothing can still be paid)
status What it means What can happen next
validating Its rows are being checked and priced. A batch of more than 100 payments is checked in the background, within a minute or two quoted by itself; canceled
quoted Checked and priced: totals, total_usd, each payment's fees. Its prices hold until quote_expires_at, an hour Add or remove payments, get a new quote, submit, cancel
pending_approval Submitted; waiting for approvals_needed approvals Approved (it moves on), rejected, canceled
awaiting_funds Approved; waiting for the wallet. waiting says why Funded (processing) by itself; canceled
processing Funded: its money is set aside in the wallet, and its fees and payments are being sent completed; canceling stops the payments not sent yet
completed Funded, and every payment is sent, failed, canceled or expired Final
canceled Canceled before it was funded: nothing was sent. By you (canceled_by a member or a key), or by payitgo (canceled_by: "system"): a send of payout requests that could not be submitted, or a batch none of whose payments could still be paid when it was funded Final
rejected An approver refused it (rejection.reason) Final

Only a validating or quoted batch takes new payments. Submitting a batch cancels the payments left out (invalid): each becomes canceled with "cancel_reason": "invalid" (its errors say why), so a submitted batch never holds a payment that cannot be paid. Until it is funded, a payment can still be canceled: the batch is totalled again (approvals already given stand), and canceled when nothing is left. To change a submitted batch otherwise, cancel it and make another.

When a batch is rejected or canceled, each of its payments not sent is canceled too, and each raises payout_item.canceled (cancel_reason rejected, with the approver's reason as cancel_note, or batch_canceled): your system hears about every payment, not only the batch.

Why a batch waits

While awaiting_funds, waiting.reason says what it waits for, and waiting.note says it for people:

reason Means What to do
funds The wallet holds less than the batch needs of at least one coin. waiting.shortfall lists what is missing: the coin, its network, the amount and the address to send it to Send that amount to that address. The batch starts by itself, usually within a minute of the money arriving on chain
daily_limit Funding it now would pass your daily limit (live only) Nothing: it is funded from 00:00 UTC. Or raise the limit (which takes 24 hours)
paused payitgo paused payouts on your account Contact support

A batch whose prices expired (quote_expires_at) by the time it is funded is priced again first, at today's network fees, so the wallet is asked for what its payments cost now: the shortfall of a batch that waits a long time can move a little. A payment that can no longer be paid then (under the minimum at today's prices, an address added to the sanctions list, your limit per payment lowered) is canceled ("cancel_reason": "invalid", canceled_by: "system", its errors saying why), its money free in the wallet; the others go on. A batch left with nothing to pay is canceled by payitgo, and your team is told.

The payments

Each row is a payment with its own status:

status What it means
validating Being checked
ready Checked and priced: it goes with the batch
invalid Left out: errors say why. It is never sent and costs nothing. Canceled (cancel_reason: invalid) when its batch is submitted; a payout request stays invalid in your list until you remove it
cooling Funded; a new recipient waits until cooling_until
queued Funded; waiting for its transfer
sending Its transfer is on its way
sent On chain: tx_id (and explorer_url, live)
failed Given up (fail_reason); its money stays in your wallet
canceled Canceled before it was sent: cancel_reason says why, canceled_by who
claimable A claim link, funded: waiting for its recipient to claim it
expired A claim link never claimed: its money is free in the wallet again

A payment whose transfer fails on chain three times fails, and so does one the token refuses outright (a recipient its issuer blocked); fail_reason says which, and the rest of the batch goes on.

Checking and pricing

Every row is checked when it is added, and again whenever the batch is quoted:

Each problem is an issue: an error leaves the payment out (invalid), a warning is worth knowing and the payment still goes.

{ "code": "wrong_network", "field": "address", "message": "this address is for Tron, not BNB Smart Chain", "hint": "pay it in a coin of that network, or check the address" }
Field Type Description
code string For your code: one of the codes below
field string, optional The field at fault: asset, amount, address, tag, reference, email or claim_assets
message string For people: what is wrong
hint string, optional For people: what to do
code Kind Meaning
unsupported_asset error No coin, an unknown coin, USDT or USDC without its network, a coin payouts do not pay in, or a claim link offering such a coin (or more than 10)
invalid_amount error The amount is 0, negative or not a number
too_precise error More decimals than the coin takes
invalid_address error Not an address of the coin's network (the message says what is wrong), the zero address, a destination tag on a coin that takes none, an XRP account that needs a tag given none or accepts only authorised senders, or an address on a claim link. As a warning: an XRP account that asks not to be sent XRP
wrong_network error An address of another network
own_wallet error Your own payout wallet
duplicate_reference error Another payment already has this reference: in this batch, or in any earlier one of the same mode
sanctioned error The address is on the US Treasury's sanctions list (OFAC SDN): it cannot be paid
contract_recipient error ETH or BNB to a contract address, which may not handle a plain transfer (live only)
below_minimum error Worth less than the smallest payment, $1; under the coin's smallest output (BTC, LTC, DOGE); under the reserve a never-funded XRP address needs; or under what a conversion takes at least
invalid_email error A claim link's email is not an email address
conversion_unavailable error The payment cannot be converted into its coin (now): pay it in the wallet's coin
over_limit error Over your limit per payment
fee_exceeds_amount error The recipient pays the fees, and they would leave less than $1
duplicate_payment warning Another row pays the same amount of the same coin to the same address
new_holder warning The first USDT sent to a Tron wallet costs more in network fees: included in its fee (live only)
activation warning A TRX payment to a Tron address not activated yet: the transfer activates it, which costs more; included in its fee (live only)
check_failed warning The address could not be read on chain just now: priced as the costlier case
new_recipient warning You never paid this address: the payment waits after the batch is funded (New recipients)

Codes may be added: treat an unknown one by its kind (in errors or in warnings) and show its message.

Fees

Each payment pays:

Fee How much
Platform fee 0.5% of the payment's amount, never less than $0.50. A rate agreed with your account replaces it
Network fee Sending it on its network, priced from the network's conditions now, at its lowest rate: its own transfer, and its share of what the batch's payments in that coin share (the transfer taking their fees to payitgo, and the network fee top-ups of each transfer of up to 50 payments)

Both are in the payment's coin, rounded up to the coin's decimals: the platform fee at the coin's dollar price when the batch was priced. A payment's fee is everything it is charged, as one sum: the two together, and for a converted payment what converting costs too (Conversion). On Bitcoin, Litecoin, Dogecoin and the XRP Ledger the network fee stays in your wallet for the transaction to spend: only the platform fee is sent to payitgo. A claim link is priced at its costliest address, and its fee taken when claimed.

On Tron, a USDT transfer's network fee is mostly its energy, priced at the network's lowest rate. Should that rate be out of reach for a moment, the transfer waits and tries again a few minutes later rather than paying more, so the network fee you were quoted is the one you pay.

Who pays the fees is the batch's fee_paid_by:

fee_paid_by The recipient receives (send_amount) The wallet gives (debit)
merchant (the default) The whole amount amount + fee
recipient amount − fee (at least $1 must be left) amount

A batch that does not say takes the default of your payout settings.

A worked example. A payment of 120 USDT. Its platform fee is 0.60 USDT: 0.5% of $120 is $0.60, over the $0.50 minimum (a payment of $60 would pay the minimum, $0.50). Its network fee depends on the network: about 1.60 USDT on Tron and about 0.012 USDT on BNB Smart Chain, at today's prices.

120 USDT On Tron (USDT_TRC20) On BNB Smart Chain (USDT_BEP20)
Platform fee 0.60 0.60
Network fee about 1.60 about 0.012
fee about 2.20 about 0.612
You pay the fees: the recipient receives / the wallet gives 120 / about 122.20 120 / about 120.612
The recipient pays them: the recipient receives / the wallet gives about 117.80 / 120 about 119.388 / 120

The batch shows the totals per coin (totals: amounts, fees, what the wallet gives and its dollar value) and total_usd, what the whole batch takes from the wallet. saved_usd adds up, over a coin's payments, what sending each with its batch saves in network fees against a transfer of its own.

The fees are sent first. When a batch is funded, its fees in each coin are sent from the wallet to payitgo in one transfer, then its payments. A claim link's fee goes in a transfer of its own when it is claimed.

A payment never sent gets its fee back. A payment canceled, or failed, before its batch's fees were sent is simply left out of that fee transfer. One canceled or failed after earns a fee credit (fee_credit): its platform fee, and its network fee too unless a transfer for it was already broadcast. Credits are taken off your next batch's fees in the same coin. You only ever pay for what was sent.

Withdrawing from the wallet pays the network fee of two transfers (Withdrawals), nothing else.

Pay someone whose wallet address you don't know: a payment with "claim": true (or with claim_assets) becomes a claim link. With an email, payitgo emails the recipient the link; they open it, choose the coin and enter the address of their wallet, confirm with a code sent to that email, and the payment goes there. Without an email, share the link yourself (the payment's claim.url, the hosted claim page) and give the recipient its PIN (claim.pin, 6 digits) another way: a call, a text message, in person. The recipient enters the PIN to claim, so a link that is lost or forwarded pays no one on its own.

A claim link is only ever made when you ask for it: a payment with an email but no address and no claim is refused for its missing address, never turned into a link (a system that lost an address would otherwise pay whoever holds the email). A claim link given an address too is refused (invalid_address, "a claim link has no address: its recipient chooses one").

curl -s https://api.payitgo.co/v1/payout-batches \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"reference": "prizes-2026-10", "items": [{"reference": "prize-ana", "asset": "USDT_TRC20", "amount": "250", "name": "Ana Silva", "email": "[email protected]", "claim": true, "claim_assets": ["USDT_BEP20", "USDC_BEP20"], "note": "Cup final, 1st place"}]}'

The payment carries claim:

Field Type Description
email string, optional Where the link and the codes are emailed
assets array of strings The other coins it can be received in
url string, optional The link (the hosted claim page). Share it when there is no email. Shown only to those who may make payouts (an API key with payouts on, an owner or finance member) and in webhooks. Absent in a quote, and in the dashboard's live updates (read the payment)
pin string, optional A link without an email: the PIN its recipient enters to claim it. Give it apart from the link. Shown where url is
pin_locked boolean, optional true once 5 wrong PINs were entered: make a new PIN to unlock it
expires_at string, optional When the link ends, once funded
claimed_at string, optional When it was claimed
funded_asset, funded_amount string, decimal string, optional What it was funded with, when its recipient chose another coin: the payment's asset and amount are then the coin and amount chosen
emailed integer Emails sent so far (the link, the reminder, resends)
extended integer How many times the link was extended

From the dashboard, the team can email the link again (POST /merchant/payout-items/{id}/resend-claim, live only, at most 5 emails in all, an hour apart), extend it (POST /merchant/payout-items/{id}/extend-claim {"days": 7}, 1 to 30 days, at most 3 times), give a link without an email a new PIN (POST /merchant/payout-items/{id}/new-claim-pin), or cancel it like any payment (Mass payouts in the dashboard). When it is claimed, payout_item.claimed is sent with the payment as it now is (coin, address and fee chosen).

In test mode nothing is emailed: open the link's url yourself; the emailed code is always 000000, and a link without an email takes its pin, as in live mode.

Conversion

Pay recipients in coins your wallet doesn't hold: with conversion on, every payment in another coin than your convert-from coin is paid from that coin, converted on its way. Keep one coin in the wallet (USDT on Tron, by default), and pay in BTC, ETH, USDC on Ethereum or any payout coin.

Turn it on in your payout settings ("convert": "convert", and convert_from, a payout coin, USDT_TRC20 by default), or for one batch or quote (convert and convert_from in its body). Payments in the convert-from coin itself are paid directly; claim links are never converted. The payout requests list follows your settings.

A converted payment's own fields are in the wallet's coin: asset is the convert-from coin, and amount, fee (what converting costs included) and debit are in it. The recipient's side is in convert, and address is the recipient's address on convert.to's network:

Field Type Description
convert.to string The coin the recipient receives (what the row asked for)
convert.ticker, convert.network string That coin for people
convert.to_amount decimal string What you asked to pay, in that coin
convert.estimate decimal string What the recipient should receive, at today's rate
convert.amount_out decimal string, optional What they received, once paid

How it is priced. The payment sends the amount's worth, plus what converting costs and a 0.5% buffer for the rate moving, so the recipient receives about the amount asked, usually a little more; the rate when it is sent decides. With the recipient paying the fees, they receive what is left after all of them. A payment under the conversion's smallest amount is refused (below_minimum); a pair that cannot be converted gets conversion_unavailable. A batch with converted payments holds its prices 15 minutes instead of an hour.

How it is sent. Converted payments go one at a time. Should a conversion fail and its coins come back, they come back to your payout wallet and the payment fails, its fail_reason saying how much came back.

In test mode conversions are priced at an example cost ($1 plus 1% of the amount) and paid on paper.

Schedules

Make the same batch again and again (payroll, a monthly prize, a weekly commission run) with a schedule, set up in the dashboard: its payments, and when it runs.

Field Type Description
name string Your name for it; each run's batch is named "{name} · {date}"
cadence string weekly, biweekly (every other week, counted from its first run) or monthly (the default)
weekday integer Weekly and biweekly: 0 (Sunday) to 6 (Saturday)
day integer Monthly: 1 to 28, or -1 for the month's last day (default 1)
at string The time of day, HH:MM (24 hours; default 09:00)
timezone string An IANA time zone (Africa/Harare, Europe/London; default UTC): the run keeps its wall-clock time across daylight saving changes
fee_paid_by string merchant or recipient (default: your payout settings')
auto_submit boolean Submit each run's batch (approvals and limits apply as to any batch); off, the batch waits quoted for someone to submit it
remind_days integer 0 to 7: that many days before a run your wallet couldn't cover, your team is told what to top up
items array Its payments, up to 1000, as in a batch (claim links included)
csv string Instead of items: a payments file's text

The payments are checked when the schedule is saved: a wrong row is refused (400 row 3: this address is for Tron, not BNB Smart Chain).

Each run makes a batch (source: "schedule", reference schedule-{id}-{YYYY-MM-DD}, each payment's reference with -{YYYY-MM-DD} added, so a run is never made twice), checks and prices it, and submits it when auto_submit is on. Whoever last set the schedule's payments (edited_by) made the batch: they cannot approve it, someone else does. A run that cannot be made (a row no longer valid, the batch over your limit) is noted in the schedule's last_error, and your team is notified. Each run sends a payout_schedule.run webhook: the schedule without its items (id, livemode, name, status, cadence, weekday, day, at, timezone, fee_paid_by, auto_submit, remind_days, totals, next_run_at, last_run_at, last_batch_id, last_error, runs). remind_days before a run the wallet couldn't cover, your team gets a "Top up for scheduled payouts" notification and a payout_schedule.funding_needed webhook, {"schedule", "shortfall"}, saying what to send where.

Pausing a schedule stops its runs; resuming it starts again from the next of its days (runs missed meanwhile are not made). Deleting it keeps the batches it made.

The schedule object: id, livemode, name, status (active or paused), cadence, weekday, day, at, timezone, fee_paid_by, auto_submit, remind_days, items, totals (per coin: asset, ticker, items, amount; fees are priced at each run), next_run_at, last_run_at, last_batch_id, last_error, runs, created_by, edited_by, members (their emails, as on a batch), created_at, updated_at. The routes (/merchant/payout-schedules) are in Mass payouts in the dashboard.

Approvals and limits

Your payout settings decide who approves a batch, how much may leave, and how new recipients and API keys are treated. The owner reads and changes them in the dashboard, with their password and two-factor code (Payout settings).

Setting Default What it does
approvals 0 How many team members, other than whoever made or submitted the batch, approve it before it is funded: 0, 1 or 2
approver_roles ["owner"] Who may approve (and reject, and release a new recipient's payment): owner, finance, or both
two_factor false Approving, withdrawing, releasing and changing the payout settings need the person's two-factor code: someone without two-factor sign-in cannot. Turning it on needs the owner's code
per_payout_max_usd "0" (none) The most one payment may be worth: a row over it is over_limit
batch_max_usd "0" (none) The most a batch may take from the wallet (total_usd): submitting a larger one is refused
daily_max_usd "0" (none) The most funded in a UTC day, live only: a batch that would pass it waits until 00:00 UTC (daily_limit)
new_recipient_hours 0 (none) How long a payment to an address you never paid waits after its batch is funded, up to 168 (New recipients)
api_auto_submit false A batch made with an API key skips approval when it is submitted, unless it pays a new recipient (the limits still apply)
api_require_allowlist false Only API keys limited to IP addresses may make payouts (The API)
auto_send_minutes 0 (send by hand) Your payout requests send themselves once the oldest valid one has waited this long, 1 to 1440 minutes (auto-send)
fee_paid_by "merchant" Who pays the fees of a batch that does not say: merchant or recipient
convert "same_network" same_network: each payment is paid in its own coin, from the wallet's balance of that coin. convert: every payment in another coin than convert_from is paid from convert_from, converted on its way (Conversion)
convert_from "USDT_TRC20" The coin conversions are paid from

Stricter now, looser in 24 hours. A change that tightens a rule (more approvals, a lower limit, a longer wait, two-factor on, an approver role removed, auto-submit off, the IP rule on, auto-send off or longer) is in force at once. One that loosens a rule waits 24 hours (pending_controls, in force from its effective_at), so whoever might take over the account cannot loosen the rules and pay out at once. Meanwhile the stricter rule stays in force. A change made while one waits replaces it, and its 24 hours start again. fee_paid_by, convert and convert_from are no safeguards: they change at once.

The approvals are made in the dashboard, by a person: Approve asks for their password, and their two-factor code when they have two-factor sign-in. An API key never approves. With 2 approvals, make sure two people other than the batch's maker can approve.

New recipients

With new_recipient_hours set, a payment to an address you never paid before (and that is not one of your own payout addresses) waits that long after its batch is funded: it is cooling, until cooling_until. It warns of it while the batch is quoted (new_recipient). A batch made with an API key and paying a new recipient always waits for its approvals, even with api_auto_submit on.

An approver can let one go sooner from the dashboard (Release, with their password and code). Canceling it keeps its money in the wallet.

Sanctions screening

Every recipient's address is checked against the US Treasury's sanctions list (OFAC's SDN list, the digital currency addresses in it), which payitgo downloads again every day. A listed address is refused (sanctioned), its payment is left out, and the refusal is recorded in your audit log. The check runs whenever a batch is quoted and again when a batch waiting for funds is priced again.

References and retries

CSV files

In the dashboard, a batch can be made from a CSV file (POST /merchant/payout-batches/csv, the file as the body, up to 16 MiB and 10,000 rows). Start from the template, GET /merchant/payout-batches/template.csv:

reference,asset,address,amount,name,email,note
payroll-2026-10-ana,USDT_TRC20,TR7mNkucnhLZoJQLLJ6DR88jpLQufWxUJi,1500,Ana Silva,[email protected],October salary
prize-cup-final-1,USDT_BEP20,0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed,250,Player One,,Cup final 1st place

The first row names the columns, in any order and any case, with spaces or dashes for underscores. asset, address and amount are required (address may be left out when the file has a claim column; its rows with claim set to yes are claim links):

Column Also read from Value
reference ref, id, external_id, employee_id, player_id Your id for the payment
asset coin, token, currency A payout coin; or USDT, USDC or DAI with a network column
network chain Completes an asset given without its network: TRC20, TRC-20 or Tron; BEP20, BEP-20, BSC or BNB Smart Chain; ERC20, ERC-20 or Ethereum
address wallet, wallet_address, recipient_address, destination The recipient's address
tag destination_tag, memo_tag XRP only: a whole number
amount value, sum The amount in the coin (below for the decimal mark)
name recipient, recipient_name, full_name The recipient's name
email recipient_email Their email
note description, memo, comment Your note
claim claim_link yes, y, true, 1 or x: a claim link without an address
claim_assets claim_coins A claim link's other coins, separated by spaces, | or / (USDT_BEP20 USDC_BEP20)
Status Message Cause
400 the file is empty or not CSV
400 the header row has no amount column (it needs asset, address and amount; reference, name, email, note, claim and claim_assets are optional; address may be left out when there is a claim column) A required column is missing
400 line 7: parse error on line 7, column 12: bare " in non-quoted-field A line the CSV reader cannot read (a stray quote, say)
400 the file has no payment · a batch holds at most 10000 payments

GET /merchant/payout-batches/{id}/items.csv downloads a batch's results: row, reference, name, email, asset, address, tag, amount, fee, send_amount, debit, status, tx_id, explorer_url, fail_reason, errors, warnings, claim_url, claim_pin, claim_expires_at, convert_to, convert_amount, convert_estimate, convert_amount_out (each issue as code: message, separated by ; ; fee is the payment's whole fee). A cell that a spreadsheet would read as a formula starts with '.

The API

Your server makes batches with an API key, like payments. The key decides the mode: a test key makes test batches, paid from the test wallet on paper.

Payouts are off for every key until the owner turns them on. Reading (batches, payments, the wallet) works with any key, read-only ones included; making, adding to, quoting again, submitting and canceling batches, quotes and test top-ups need a key with payouts on. The owner turns it on in the dashboard (Update a key), and can limit the key to your servers' IP addresses (allowed_ips): a key limited so is refused from any other address, on every route. With api_require_allowlist in your payout settings, only keys limited to IP addresses may make payouts.

Every route that takes a batch id answers 404 not found for a batch of the other mode than the key's: a test key never reads or changes a live batch, and the other way round.

Method Path Key Body Response
POST /v1/payouts payouts a payment: reference, asset, address, tag, amount, name, email, note, claim, claim_assets, webhook_url 201 {"item": payment}; 200 for a reference used already
GET /v1/payouts/inbox?status=&limit=&offset= any 200 {"inbox": batch, "coverage", "items", "total", "limit", "offset"}
POST /v1/payouts/send payouts ids (optional) 200 {"batch": batch}
DELETE /v1/payouts/{id} payouts 200 {"item": payment}
POST /v1/payouts/remove payouts ids (1 to 1000) 200 {"results": [{"id", "item"} or {"id", "error"}, …]}
GET /v1/payouts?status=&updated_since=&cursor=&limit= any 200 {"items": [payment, …], "has_more", "next_cursor"}
GET /v1/events?type=&after=&created_after=&limit= any account key 200 {"events": [event, …], "has_more"}
POST /v1/payout-quotes payouts fee_paid_by, convert, convert_from, items (1 to 100) 200 {"quote": {…}, "items": [payment, …]}, nothing kept
POST /v1/payout-batches payouts reference, name, fee_paid_by, webhook_url, convert, convert_from, items (1 to 1000) 201 {"batch": batch}; 200 for a reference used already
GET /v1/payout-batches?status=&limit=&offset= any 200 {"batches": [batch, …], "total", "limit", "offset"}
GET /v1/payout-batches/{id} any 200 {"batch": batch}
GET /v1/payout-batches/{id}/items?status=&limit=&offset= any 200 {"items": [payment, …], "total", "limit", "offset"}
POST /v1/payout-batches/{id}/items payouts items (1 to 1000) 200 {"batch": batch}
POST /v1/payout-batches/{id}/quote payouts 200 {"batch": batch}
POST /v1/payout-batches/{id}/submit payouts 200 {"batch": batch}
POST /v1/payout-batches/{id}/cancel payouts 200 {"batch": batch}
GET /v1/payouts?reference= any 200 {"item": payment}
GET /v1/payouts/{id} any 200 {"item": payment}
GET /v1/payout-wallets any 200 {"wallets": [wallet, …]}
POST /v1/payout-wallets/{asset}/simulate-top-up payouts, test key amount 200 {"wallets": [wallet, …]}

The dashboard has the same routes under /merchant (with ?mode=test; payout requests are under /merchant/payout-requests, payments under /merchant/payout-items), plus approving, rejecting, removing a payment, CSV files, canceling or releasing one payment, withdrawals and the payout settings (Mass payouts in the dashboard).

Quote payments

POST /v1/payout-quotes · a key with payouts on

What up to 100 payments would cost, checked and priced exactly as in a batch, without keeping anything: use it to show a recipient their fee before they ask for a withdrawal. The body takes fee_paid_by, convert, convert_from and items, as a batch does.

curl -s https://api.payitgo.co/v1/payout-quotes \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"fee_paid_by": "recipient", "items": [{"asset": "USDT_BEP20", "address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", "amount": "120"}]}'

200 OK: quote holds the totals a batch of them would have (livemode, fee_paid_by, convert, convert_from (only when converting), totals, total_usd, counts), and items the payments as they would be, each with its fee and issues. Nothing is kept: the payments have no id, merchant_id, batch_id or times.

{
  "quote": {
    "livemode": true,
    "fee_paid_by": "recipient",
    "convert": "same_network",
    "totals": [
      { "asset": "USDT_BEP20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg", "chain": "bsc", "network": "BNB Smart Chain (BEP-20)", "items": 1, "amount": "120", "send_amount": "119.38816579", "fee": "0.61183421", "debit": "120", "debit_usd": "120", "saved_usd": "0" }
    ],
    "total_usd": "120",
    "counts": { "total": 1, "invalid": 0, "ready": 1, "cooling": 0, "queued": 0, "sending": 0, "sent": 0, "failed": 0, "canceled": 0, "claimable": 0, "expired": 0 }
  },
  "items": [
    {
      "livemode": true,
      "row": 1,
      "recipient": {},
      "asset": "USDT_BEP20",
      "ticker": "USDT",
      "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg",
      "chain": "bsc",
      "address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "amount": "120",
      "send_amount": "119.38816579",
      "fee": "0.61183421",
      "debit": "120",
      "usd_rate": "1",
      "amount_usd": "120",
      "status": "ready",
      "errors": [],
      "warnings": []
    }
  ]
}
Status Message Cause
400 quote between 1 and 100 payments
400 fee_paid_by must be merchant or recipient

Create a batch

POST /v1/payout-batches · a key with payouts on

Field Type Required Description
reference string no, but send one Your id for the batch, unique in its mode. A reference used already returns that batch (retries)
name string no Your name for it, for people ("October payroll")
fee_paid_by string no merchant (fees on top) or recipient (fees out of each amount). Default: your payout settings
webhook_url string no An https:// URL on a public host: this batch's events go there instead of your account's webhook URL (Webhooks)
convert, convert_from string no same_network or convert, and the coin conversions are paid from (Conversion). Default: your payout settings
items array yes 1 to 1000 payments (below). Add more with /items, up to 10,000

Each payment:

Field Type Required Description
asset string yes A payout coin (any case). With conversion on, any payout coin: it is the coin the recipient receives
address string yes, unless a claim link The recipient's address on the coin's network
amount decimal string yes How much of the coin. With fee_paid_by: "recipient" the fees come out of it
reference string no Your id for this payment, unique in its mode (retries)
name, email, note string no Who it is for, and why: kept with it, shown in the dashboard and the results file. A claim link's link is emailed there; otherwise nothing is sent to the email
tag integer no XRP only: the destination tag the recipient's address needs
claim boolean no true makes it a claim link, without an address (with no email: you share its link)
claim_assets array of strings no The other coins its recipient may choose (up to 10). Giving them makes the payment a claim link
webhook_url string no An https:// URL on a public host: this payment's events go there instead of the batch's webhook_url or your account's URL. At most 10 different URLs in one call
curl -s https://api.payitgo.co/v1/payout-batches \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "reference": "payroll-2026-10",
    "name": "October payroll",
    "items": [
      {"reference": "payroll-2026-10-ana", "asset": "USDT_TRC20", "address": "TR7mNkucnhLZoJQLLJ6DR88jpLQufWxUJi", "amount": "120", "name": "Ana Silva", "email": "[email protected]", "note": "October salary"},
      {"reference": "payroll-2026-10-ben", "asset": "USDT_BEP20", "address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", "amount": "250", "name": "Ben Okafor"},
      {"reference": "payroll-2026-10-chen", "asset": "USDT_BEP20", "address": "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7", "amount": "75", "name": "Chen Wei"}
    ]
  }'

201 Created. Up to 100 payments are checked while the call waits, and the batch comes back quoted:

{
  "batch": {
    "id": "6a1f3c8e2d9b4a7051c3e8f2",
    "merchant_id": "3f8a1c27b94e05d6a2c71e90",
    "livemode": true,
    "reference": "payroll-2026-10",
    "name": "October payroll",
    "source": "api",
    "inbox": false,
    "open": false,
    "created_by": "5e1a9c3d7b2f40e8a6c4d2b1",
    "by_api_key": true,
    "fee_paid_by": "merchant",
    "convert": "same_network",
    "status": "quoted",
    "totals": [
      { "asset": "USDT_TRC20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_TRC20.svg", "chain": "tron", "network": "Tron (TRC-20)", "items": 1, "amount": "120", "send_amount": "120", "fee": "2.202417", "debit": "122.202417", "debit_usd": "122.2", "saved_usd": "0" },
      { "asset": "USDT_BEP20", "ticker": "USDT", "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg", "chain": "bsc", "network": "BNB Smart Chain (BEP-20)", "items": 1, "amount": "250", "send_amount": "250", "fee": "1.26183421", "debit": "251.26183421", "debit_usd": "251.26", "saved_usd": "0" }
    ],
    "counts": { "total": 3, "invalid": 1, "ready": 2, "cooling": 0, "queued": 0, "sending": 0, "sent": 0, "failed": 0, "canceled": 0, "claimable": 0, "expired": 0 },
    "total_usd": "373.46",
    "quote_expires_at": "2026-10-20T10:15:02.41Z",
    "approvals_needed": 0,
    "approvals": [],
    "members": {},
    "created_at": "2026-10-20T09:15:01.87Z",
    "updated_at": "2026-10-20T09:15:02.41Z"
  }
}

The third row pays a Tron address in a BNB Smart Chain coin: it is invalid, left out of totals (list the payments to see why). A batch of more than 100 payments comes back validating, its counts.total and counts.ready both the number of payments until it is checked; a payout_batch.quoted webhook says when it is ready, within a minute or two.

Status Message Cause
400 a batch needs at least one payment No items
400 send at most 1000 payments in one call: add the rest with /items, or upload a CSV file Over 1000 items
400 fee_paid_by must be merchant or recipient
400 webhook_url: url must use https (and the other URL errors) An unusable webhook_url, the batch's or a payment's
400 payments may name at most 10 different webhook_urls in one call
400 convert must be same_network or convert · convert_from: payouts are not paid from SHIB_ERC20 (pay from …) An unusable convert or convert_from
400 invalid request body Not JSON, or a field of the wrong type
403 this api key cannot make payouts: turn payouts on for it in the dashboard The key's payouts is off
403 your payout rules require keys limited to IP addresses: set this key's allowed IPs api_require_allowlist is on and the key has no allowed_ips

A row's own problems never fail the request: they are its errors.

Add payments

POST /v1/payout-batches/{id}/items · a key with payouts on

{"items": [payment, …]}, 1 to 1000 at a time, to a validating or quoted batch, up to 10,000 payments in all. They are numbered after the last row, and the whole batch is checked and priced again (in the background past 100 payments). 200 OK: {"batch": batch}.

Status Message Cause
400 add between 1 and 1000 payments at a time · a batch holds at most 10000 payments
404 not found No such batch
409 a submitted batch cannot change: cancel it and make another It was submitted

Submit a batch

POST /v1/payout-batches/{id}/submit · a key with payouts on

Sends a quoted batch on: its ready payments go, the invalid ones stay out. With approvals in your payout settings it becomes pending_approval (unless api_auto_submit lets it skip them); otherwise awaiting_funds, and it is funded at once if the wallet covers it (processing). When the wallet falls short, the answer already says by how much:

{
  "batch": {
    "id": "6a1f3c8e2d9b4a7051c3e8f2",
    "status": "awaiting_funds",
    "waiting": {
      "reason": "funds",
      "note": "top up the payout wallet: 122.202417 USDT_TRC20 on Tron (TRC-20), 251.26183421 USDT_BEP20 on BNB Smart Chain (BEP-20)",
      "since": "2026-10-20T09:16:40.12Z",
      "shortfall": [
        { "asset": "USDT_TRC20", "network": "Tron (TRC-20)", "amount": "122.202417", "address": "TMkq7dZ3vN8xR2pB6cH9sJ4fL1wY5tGaQe" },
        { "asset": "USDT_BEP20", "network": "BNB Smart Chain (BEP-20)", "amount": "251.26183421", "address": "0x8F3a6B2c9D1e4F7a0B5c8D2e6F9a1B4c7D0e3F6a" }
      ]
    },
    "approvals_needed": 0,
    "submitted_by": "5e1a9c3d7b2f40e8a6c4d2b1",
    "submitted_at": "2026-10-20T09:16:40.09Z",
    "…": "…"
  }
}
Status Message Cause
400 no valid payment to send Every payment is invalid
400 this batch takes $373.46, over your limit of $250.00 per batch Over batch_max_usd
404 not found
409 the batch is still being checked validating
409 its prices expired: get a new quote first Past quote_expires_at (an hour after it was quoted; 15 minutes with converted payments): get a new quote, then submit
409 the batch was submitted already

Get a new quote

POST /v1/payout-batches/{id}/quote · a key with payouts on

Checks and prices a batch not submitted yet (validating or quoted) again, at today's prices: its prices then hold for another hour (15 minutes with converted payments). Use it when submitting answers 409 its prices expired: get a new quote first. 200 OK: {"batch": batch}, quoted (or validating past 100 payments, payout_batch.quoted following). 409 a submitted batch cannot change: cancel it and make another once submitted; 404 not found for another mode's batch.

Cancel a batch

POST /v1/payout-batches/{id}/cancel · a key with payouts on

Before it is funded (validating, quoted, pending_approval, awaiting_funds), the whole batch is canceled and nothing leaves the wallet. Once it is processing, the payments not sent yet are canceled (one whose transfer is being signed at that moment may still go): their money stays in the wallet, their fees come back (Fees), and the batch completes with what was sent. 200 OK: {"batch": batch}. 409 the batch is completed (or canceled, rejected) when there is nothing left to cancel.

List batches

GET /v1/payout-batches?status=&limit=&offset= · any key

The key's mode's batches, newest first. status filters on one status; limit is 1 to 200 (50 by default).

Get a batch

GET /v1/payout-batches/{id} · any key

200 OK: {"batch": batch}. 404 not found for another mode's batch.

List a batch's payments

GET /v1/payout-batches/{id}/items?status=&limit=&offset= · any key

In row order. status takes one status or several, comma-separated (?status=invalid, ?status=sent,failed).

curl -s 'https://api.payitgo.co/v1/payout-batches/6a1f3c8e2d9b4a7051c3e8f2/items?status=invalid' -H "Authorization: Bearer $PAYITGO_KEY"

200 OK:

{
  "items": [
    {
      "id": "4e6a8c0b2d4f6a1c3e5b7d9a",
      "merchant_id": "3f8a1c27b94e05d6a2c71e90",
      "livemode": true,
      "batch_id": "6a1f3c8e2d9b4a7051c3e8f2",
      "reference": "payroll-2026-10-chen",
      "row": 3,
      "recipient": { "name": "Chen Wei" },
      "asset": "USDT_BEP20",
      "ticker": "USDT",
      "icon_url": "https://api.payitgo.co/coins/USDT_BEP20.svg",
      "chain": "bsc",
      "address": "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7",
      "amount": "75",
      "send_amount": "0",
      "fee": "0",
      "debit": "0",
      "usd_rate": "0",
      "amount_usd": "0",
      "status": "invalid",
      "errors": [
        { "code": "wrong_network", "field": "address", "message": "this address is for Tron, not BNB Smart Chain", "hint": "pay it in a coin of that network, or check the address" }
      ],
      "warnings": [],
      "created_at": "2026-10-20T09:15:01.88Z",
      "updated_at": "2026-10-20T09:15:02.4Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

Get a payment

GET /v1/payouts/{id} or GET /v1/payouts?reference=payroll-2026-10-ana · any key

One payment, by its id or by your reference. By reference, the payment that carries it now; if none does, the latest that gave it back (canceled, failed or expired, with it as released_reference): what became of a request sent with it. A sent payment:

{
  "item": {
    "id": "8b2d4f6a1c3e5a7b9d0f2c4e",
    "merchant_id": "3f8a1c27b94e05d6a2c71e90",
    "livemode": true,
    "batch_id": "6a1f3c8e2d9b4a7051c3e8f2",
    "reference": "payroll-2026-10-ana",
    "row": 1,
    "recipient": { "name": "Ana Silva", "email": "[email protected]", "note": "October salary" },
    "asset": "USDT_TRC20",
    "ticker": "USDT",
    "icon_url": "https://api.payitgo.co/coins/USDT_TRC20.svg",
    "chain": "tron",
    "address": "TR7mNkucnhLZoJQLLJ6DR88jpLQufWxUJi",
    "amount": "120",
    "send_amount": "120",
    "fee": "2.202417",
    "debit": "122.202417",
    "usd_rate": "1",
    "amount_usd": "120",
    "status": "sent",
    "errors": [],
    "warnings": [],
    "tx_id": "3c9f1a7e5b2d8c4f6a0e9b3d7c1f5a2e8b4d0c6f9a3e7b1d5c8f2a6e0b4d9c3f",
    "explorer_url": "https://tronscan.org/#/transaction/3c9f1a7e5b2d8c4f6a0e9b3d7c1f5a2e8b4d0c6f9a3e7b1d5c8f2a6e0b4d9c3f",
    "sent_at": "2026-10-20T09:31:12.6Z",
    "created_at": "2026-10-20T09:15:01.88Z",
    "updated_at": "2026-10-20T09:31:12.6Z"
  }
}
Status Message Cause
404 not found No such payment in the key's mode

List payments

GET /v1/payouts?status=&updated_since=&cursor=&limit= · any key

Every payment of the key's mode, whatever its batch (requests in your list, removed ones, every batch's), oldest change first: to bring your system back in sync after it was down, or to check it is.

Query Description
status Comma-separated statuses (sent,failed,canceled,expired for the ended ones); all when left out
updated_since RFC 3339 time: payments changed at or after it
cursor The next_cursor of the page before: the payments changed after the last one it listed
limit 1 to 200; 50 by default
{
  "items": [ { "id": "5c7e9a1b3d5f7a9c1e3b5d7f", "reference": "wd-88231", "status": "sent", "updated_at": "2026-10-20T14:31:12.6Z", "…": "…" } ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMC0yMFQxNDozMToxMi42WnwyYzVlN2E5YjFkM2Y1YTdjOWUxYjNkNWY"
}

Read with updated_since the first time, then with cursor while has_more; keep the last next_cursor to start from next time. Changes of the last few seconds are left for the next read, so a cursor never skips one.

Status Message Cause
400 updated_since must be an RFC 3339 time, such as 2026-10-20T09:00:00Z
400 limit must be 1 to 200
400 cursor is not one this API gave

The batch object

Field Type Description
id string The batch's id
merchant_id string Your account
livemode boolean false for a test batch
reference string, optional Your id for it, unique in its mode
name string Your name for it; "" when none
source string api, dashboard, csv, requests (made of payout requests) or schedule (a schedule's run)
inbox boolean Made of payout requests: your list of them, or a batch of sent ones
open boolean Your list of payout requests itself (one per mode): never submitted, never listed among batches
created_by string The team member who made it, the API key's id (by_api_key), auto-send for payout requests that sent themselves, or requests for the list
by_api_key boolean Whether an API key made it (true for auto-send too)
fee_paid_by string merchant or recipient
webhook_url string, optional Where its events go instead of your account's URL
convert, convert_from string Its conversion rule (same_network or convert) and, when converting, the coin converted from
status string A batch's life
waiting object, optional While awaiting_funds and waiting: reason (funds, daily_limit, paused), note for people, since, and for funds the shortfall: [{"asset", "network", "amount", "address"}], what to send where
totals array Its payments that go, per coin: asset, ticker and icon_url (what people call the coin, and its icon), chain, network, items (how many), amount, send_amount, fee (their fees, as one sum), debit (what the wallet gives), debit_usd, and saved_usd (what sending them together saves in network fees)
counts object Its payments by status: total, invalid, ready, cooling, queued, sending, sent, failed, canceled, claimable, expired
total_usd decimal string What it takes from the wallet, in dollars
quote_expires_at string, optional Its prices hold until then
approvals_needed integer Approvals it needs, set when it is submitted
approvals array [{"user_id", "at"}]: who approved it, and when
rejection object, optional {"by", "at", "reason"} once rejected
submitted_by, submitted_at string, optional Who submitted it (a member's id or a key's), and when
funded_at, completed_at string, optional When it was funded, and completed
canceled_by, canceled_at string, optional Who canceled it, and when (also on a processing batch whose remaining payments were canceled)
members object The emails of the team members it names (created_by, submitted_by, approvals[].user_id, rejection.by, canceled_by), by id, as they were when each acted: {"9b2e4c71d8a05f3e6c1a7b24": "[email protected]"}. A member who has left the team stays named. API keys, auto-send and the platform are not in it; {} when no member acted
created_at, updated_at string

The payment object

Field Type Description
id string The payment's id
merchant_id string Your account
batch_id string, optional Its batch: your list's id while it is a payout request waiting there. Absent for a request removed from the list
livemode boolean
reference string, optional Your id for it, unique in its mode
refused_reference string, optional A reference it was given that another payment carries: it keeps none, and is invalid
released_reference string, optional Once it is canceled or failed (never sent): the reference it had, given back for a new payment to use; reference is then empty
row integer Its place in the batch, from 1; payments added later follow the last row
recipient object name, email, note, as you gave them (each left out when empty)
asset, chain string The coin, and its network
ticker, icon_url string What people call the coin ("USDT") and its icon, an SVG image. Absent for a coin payouts do not know
address string The recipient's address, as it is sent to (an EVM address in its checksummed form)
tag integer, optional A destination tag
amount decimal string What you asked to pay
send_amount decimal string What reaches the recipient
fee decimal string Everything it is charged, in the coin, as one sum: the platform fee and the network fee, and for a converted payment what converting costs
debit decimal string What it takes from the wallet
usd_rate decimal string The coin's dollar price when it was priced
amount_usd decimal string amount in dollars, to the cent
status string The payments
errors, warnings array Its issues; [] when none
cooling_until string, optional A new recipient's payment waits until then
tx_id string, optional Its transaction, once sent (made up in test mode)
explorer_url string, optional The transaction on the block explorer. Live only
fail_reason string, optional Why it failed
cancel_reason string, optional Why it was canceled: removed (taken out of your list of payout requests), canceled (that payment alone, by your team or a key), batch_canceled (its batch was canceled), rejected (its batch was rejected), invalid (no longer payable when its batch was submitted or funded: errors say why)
cancel_note string, optional With rejected: the approver's reason
canceled_by, canceled_at string, optional Who canceled it (a team member's id, an API key's id, or system: payitgo itself), and when
members object, optional The email of the team member in canceled_by, by id, as on batches
webhook_url string, optional Where its events go instead of its batch's webhook_url or your account's URL
claim object, optional A claim link's state: email, assets, url, pin, pin_locked, expires_at, claimed_at, funded_asset, funded_amount, emailed, extended
convert object, optional A converted payment: to, ticker, network, to_amount, estimate, amount_out
fee_credit decimal string, optional The fee given back for a payment never sent, taken off your next batch's fees in its coin
sent_at string, optional When it was sent
created_at, updated_at string

Before it is checked (validating) and when it is invalid, its fee and dollar figures are "0".

Webhooks

Each step of a batch and of each payment raises a webhook, signed like every other with the secret of its mode. Where they go:

Event Sent when data
payout_batch.quoted It was checked and priced: made, payments added or removed, quoted again Batch
payout_batch.pending_approval Submitted; it waits for approvals Batch
payout_batch.approved It got its last approval: it is funded next (payout_batch.processing), or waits (payout_batch.awaiting_funds) Batch
payout_batch.rejected An approver refused it (rejection.reason) Batch
payout_batch.awaiting_funds It cannot be funded yet: waiting says why (funds, with the shortfall to send; daily_limit; paused). Sent again whenever the reason changes. A batch the wallet covers at once goes straight to payout_batch.processing Batch
payout_batch.processing Funded: its fees and payments are being sent Batch
payout_batch.completed Funded, and every payment is sent, failed, canceled or expired (counts) Batch
payout_batch.canceled Canceled before it was funded: by your team or a key, or by payitgo (canceled_by: "system": a send of requests that could not be submitted, or nothing left that could be paid) Batch
payout_item.processing A payout request left your list for a batch (sent from the dashboard, by POST /v1/payouts/send or by auto-send): batch_id is the new batch Payment
payout_item.returned A sent payout request came back to your list: its batch could not be submitted, or it was no longer valid when priced again (status invalid, errors say why). Not final Payment
payout_item.sent A payment is on chain (tx_id) Payment
payout_item.failed A payment was given up (fail_reason); its money stays in the wallet Payment
payout_item.canceled A payment was canceled before it was sent, whatever canceled it: cancel_reason (removed, canceled, batch_canceled, rejected with cancel_note, invalid) and canceled_by say why and who Payment
payout_item.claimed A claim link was claimed: the payment with the coin, address and fee its recipient chose Payment
payout_item.claim_expired A claim link ended unclaimed; its money is free in the wallet again Payment
payout_schedule.run A schedule ran: its batch was made (last_batch_id), or last_error says why not The schedule, without its items
payout_schedule.funding_needed remind_days before a schedule's run the wallet couldn't cover {"schedule", "shortfall"} (the schedule without its items)
payout_wallet.withdrawn A withdrawal reached your payout address (tx_id) Withdrawal
payout_wallet.withdrawal_failed A withdrawal could not be sent (fail_reason); its money stays in the wallet Withdrawal

Taking a payout request raises no event (the answer says it all); from then on, the request's payment raises the events above.

Staying in sync

Every payment you ask for, a payout request or a batch row, ends in exactly one final state, and you hear it once, whoever acted on it: your system, a member of your team in the dashboard (removing it, rejecting or canceling its batch), or payitgo (a payment no longer payable):

Final status Event What to do in your system
sent payout_item.sent Mark it paid (tx_id)
failed payout_item.failed Not paid: its money is in your wallet. Request it again if you still want to pay it
canceled payout_item.canceled Not paid, never will be (cancel_reason). Give your client their money back, or request it again
expired payout_item.claim_expired A claim link nobody claimed

A payout request also tells you when it leaves your list (payout_item.processing) and when it comes back (payout_item.returned). A batch row is in its batch from the start: its batch's events say how the batch goes.

Replay events

GET /v1/events?type=&after=&created_after=&limit= · any key of the account (read-only ones too; a storefront's key is refused)

The mode's events, oldest first, each exactly as its webhook carried it (id, type, livemode, created_at, data), payments' and payouts' alike. Events are kept; a test ping is left out.

Query Description
type Comma-separated: exact types (payout_item.sent), or a family (payout_item.*); all when left out
after An event's id: the events after it. Keep the last one you handled
created_after RFC 3339 time: the events after it (ignored with after)
limit 1 to 200; 100 by default
curl -s "https://api.payitgo.co/v1/events?type=payout_item.*&after=4f1c2a9e7b3d5f80a2c4e6b8" \
  -H "Authorization: Bearer $PAYITGO_KEY"
{
  "events": [
    {
      "id": "9b1e3c5a7d2f4b6e8a0c1d3f",
      "type": "payout_item.canceled",
      "livemode": true,
      "created_at": "2026-10-20T15:02:44.312Z",
      "data": { "id": "5c7e9a1b3d5f7a9c1e3b5d7f", "status": "canceled", "cancel_reason": "rejected", "cancel_note": "paid by bank transfer instead", "released_reference": "wd-88231", "…": "…" }
    }
  ],
  "has_more": false
}

While has_more, read again with after the last id. Events of the last few seconds are left for the next read, so moving forward never skips one. A key that cannot make payouts gets claim links without their url and pin.

Status Message Cause
400 type must be event types, such as payout_item.sent or payout_item.*
400 created_after must be an RFC 3339 time, such as 2026-10-20T09:00:00Z
400 limit must be 1 to 200
400 after is not an event of this mode An unknown id, or the other mode's
403 a storefront's key cannot read the account's events: use an account key

In test mode

A test key (or the dashboard with ?mode=test) works with your test payout wallet: other addresses than the live ones, with balances on paper. Nothing is ever read from or sent on a chain:

Status Message Cause
400 only a test wallet is topped up on paper: use a test key A live key on /v1
400 only a test wallet is topped up on paper: add ?mode=test The dashboard route without ?mode=test
400 payouts in SHIB_ERC20 are not available yet Not a payout coin
400 amount must be over 0 and at most 10000000

Limits

What Limit
Payments in a batch 10,000
Payout requests waiting in your list 100,000; up to 10,000 go in each send
Payments in one create or add call 1,000 (a CSV file in the dashboard takes up to 10,000)
Checked while the call waits 100: a larger batch is checked in the background (validating)
Payments in a quote 100
A quote's prices Hold for an hour
The smallest payment $1
Payments in one transfer 50 (200 in one Bitcoin, Litecoin or Dogecoin transaction); one transfer at a time per wallet and network
Speed One transfer every 15 seconds per network, from one wallet: about 240 payments an hour; on Bitcoin, Litecoin and Dogecoin one transfer pays up to 200
A CSV file 16 MiB
allowed_ips 20 addresses or ranges per key
A simulated top-up 10,000,000 of the coin per call
A batch with converted payments Its prices hold 15 minutes
A claim link Works 14 days after funding; extended up to 3 times (1 to 30 days); up to 5 emails; 10 other coins
A schedule 1000 payments
/v1 requests 20 a second per key (rate limits)

Audit log

Every step is in your audit log, with who did it and the figures before and after: payout_batch.created, .items_added, .item_removed, .submitted, .approved, .rejected, .funded, .canceled, .completed, .items_canceled (payments no longer payable when it was funded); payout_request.created, payout_request.canceled, payout_requests.sent, payout_requests.returned, payout_requests.removed; payout_item.canceled, .released, .screening_hit, .claimed, .claim_resent, .claim_extended, .claim_expired, .claim_locked, .claim_pin_changed; payout_schedule.created, .updated, .paused, .resumed, .deleted, .run, .funding_needed; payout_wallet.created, .extended (a network added to it), .test_top_up, .withdrawal_requested, .withdrawal_sent, .withdrawal_failed, .idle; payout_controls.updated and .took_effect (a loosening whose 24 hours ended); api_key.updated.