Payments

A payment is one request for money from one of your customers: an amount in US dollars (or in your own currency, converted into dollars), the coin they pay with, and a deposit address only this payment uses. Your server creates it with your secret key, sends the customer to pay, and learns the outcome from webhooks.

Method Path What it does
POST /v1/payments Create a payment (or get back the open payment of the same order)
POST /v1/payments/{id}/token Set the coin of a payment created without one
POST /v1/payments/{id}/simulate Test mode: play an outcome on a test payment
GET /v1/payments/{id} Read one payment, by payment id or by your order id
GET /v1/payments List payments, newest first
GET /v1/tokens The coins your customers can pay with, with today's fees and minimums

All of them take your API key (Authorization: Bearer <key>, see Authentication). The key's mode (test or live) decides which payments you create and see.

Lifecycle

pending ─────────────────────────┐
   │                             ▼
   ├──▶ partially_paid ──▶ detecting ──▶ confirmed
   │          │                 │
   ▼          ▼                 ▼
expired    underpaid          failed
   │          │
   └────┬─────┘
        ▼   the rest arrives within 24 hours
    detecting ──▶ confirmed   ("paid_late": true)
Status Meaning Webhook sent when the payment enters it What you do
pending Created. Waiting for the customer to choose a coin (if you did not) and to pay none (no event is sent at creation) Send the customer to pay
partially_paid Less than the amount due arrived, and there is still time to send the rest payment.partially_paid Optionally tell the customer
detecting Enough arrived. Waiting for the network's confirmations payment.detecting Show "payment received, confirming". Do not ship yet
confirmed Final. The money is yours payment.confirmed Fulfil the order
expired The deadline passed and nothing counted arrived payment.expired Cancel the order, or create a new payment
underpaid The deadline passed with only part of the amount. What arrived is still settled to you payment.underpaid Refund the customer, or agree on the rest
failed The funds were seen but never became final (dropped from the chain, or not confirmed in time) payment.failed Treat as unpaid

Three more events report deposits rather than status changes: payment.wrong_asset (a deposit in another coin than the payment asked for), payment.late_deposit (a deposit after the payment closed) and, with direct settlement, settlement.completed / settlement.failed for each deposit's money. See Webhooks.

How the payment moves:

Only confirmed means you have been paid. Never fulfil an order from detecting, from a customer landing on your success_url, or from anything the browser says.

The payment object

Every endpoint on this page, and every payment.* webhook, returns this object. Here is a live payment that has been paid and confirmed:

{
  "id": "8f3a2c1e9b7d4a6f05e1c2d3",
  "merchant_id": "3f8a1c27b94e05d6a2c71e90",
  "order_id": "ORD-1001",
  "status": "confirmed",
  "deposit_address": "TNVfZ8m3qR6xW2pL9kC4hJ7bD5sYt1uAeG",
  "token": "USDT_TRC20",
  "chain": "tron",
  "livemode": true,
  "crypto_amount": "49.9",
  "received_amount": "49.9",
  "usd_amount": "49.9",
  "amount_due_usd": "49.9",
  "fees_paid_by_customer": false,
  "network_fee": "6.019231",
  "network_fee_usd": "6.03",
  "settlement": "direct",
  "received_usd": "49.9",
  "rate_locked": "1",
  "tx_id": "c192401935e4ac25bb5f7cdba7443ada0e097cfdb537710236bf7d98c60851bd",
  "deposits": [
    {
      "tx_id": "c192401935e4ac25bb5f7cdba7443ada0e097cfdb537710236bf7d98c60851bd",
      "token": "USDT_TRC20",
      "chain": "tron",
      "amount": "49.9",
      "usd_value": "49.9",
      "counted": true,
      "status": "confirmed",
      "seen_at": "2026-10-10T09:03:02.418Z",
      "confirmed_at": "2026-10-10T09:04:03.77Z",
      "from": "TL8wHk3sD9pQ2vX6mB4nJ7cR5fY1tGzEaW",
      "block": 79412538,
      "made_at": "2026-10-10T09:02:57Z"
    }
  ],
  "expires_at": "2026-10-10T09:15:00.112Z",
  "created_at": "2026-10-10T09:00:00.112Z",
  "confirmed_at": "2026-10-10T09:04:03.77Z",
  "network_fee_note": "Final in about a minute.",
  "qr_payload": "TNVfZ8m3qR6xW2pL9kC4hJ7bD5sYt1uAeG",
  "checkout_token": "6f1e2a9c4d7b3e05a8c1f2d4",
  "checkout_url": "https://pay.example.com/pay/6f1e2a9c4d7b3e05a8c1f2d4",
  "description": "2 x Acme T-shirt",
  "metadata": { "customer_id": "cus_42" },
  "customer_email": "jane@example.com",
  "success_url": "https://acme.example/thanks",
  "cancel_url": "https://acme.example/cart",
  "webhook_url": "https://acme.example/payitgo/orders/ipn"
}

Amounts are decimal strings ("49.9", "0.00117"), so no precision is lost: parse them with a decimal type, not a float. Times are RFC 3339 in UTC; the number of fractional digits varies, so use a real RFC 3339 parser. Fields marked "optional" below are left out of the JSON when they have no value.

Field Type Description
id string payitgo's id for the payment: 24 hex characters. Treat it as an opaque string
merchant_id string Your account's id
store_id string, optional The store whose key created the payment
order_id string Your order id, as you sent it (trimmed)
status string Where the payment is in its lifecycle
token_options array of TokenInfo, optional Only while token is "": the coins the customer may choose from, each with its minimum and fee. Gone once a coin is chosen
deposit_address string Where the customer sends the money. Used by this payment only, except on XRP (see destination_tag). "" until a coin is chosen
destination_tag integer, optional XRP only: the tag the customer must include, which tells payments apart on the shared XRP address
token string The coin to pay with, such as USDT_TRC20 (Currencies). "" until chosen
chain string The token's network: tron, ethereum, bsc, bitcoin, litecoin, dogecoin or xrp. "" until chosen
livemode boolean false for a test payment (no coins: paid by the outcome you simulate), true for a real one
simulation object, optional A test payment's simulation, once one is asked for: outcome (such as confirmed) and state: requested (asked for on create, waiting for the coin), running or done
crypto_amount decimal string What the customer must send, in token: amount_due_usd ÷ rate_locked, rounded up to the token's smallest unit. "0" until a coin is chosen
received_amount decimal string What has arrived so far in the payment's own token (late deposits included, other coins not)
usd_amount decimal string Your price in US dollars, as you sent it in amount_usd, or converted from price_amount
price_amount decimal string, optional Your price in your own currency, as you sent it (prices in other currencies). Absent for a payment priced in dollars
price_currency string, optional Its currency, the ISO 4217 code in upper case: EUR. Absent for a payment priced in dollars
price_rate decimal string, optional The dollar value of one unit of price_currency when the payment was created: usd_amount is price_amount × price_rate, rounded to cents. Absent for a payment priced in dollars
amount_due_usd decimal string What the customer pays, in dollars: usd_amount, or more when you pass the fees on (fees_paid_by_customer)
extra_usd decimal string, optional What of amount_due_usd the customer pays only to reach the exchange's minimum (the payment too small for its coin's conversion, the fees passed on): you receive it
fees_paid_by_customer boolean Whether the customer pays the service and network fees on top of your price (Fees and settlement)
network_fee decimal string The network fee quoted for moving this payment's money, in token, with what converting it costs when it is converted (fees). "0" until a coin is chosen, or when it could not be priced (it is then priced when the deposit settles)
network_fee_usd decimal string The same fee in dollars
settlement string Where the money goes once final: direct (your own payout address) or custodial (your payitgo balance). Your account setting when the payment was created (Fees and settlement)
received_usd decimal string The dollar value of every deposit that counts toward the payment, rounded to cents
rate_locked decimal string The USD price of one unit of token, locked when the coin was chosen. "1" for stablecoins. "0" until a coin is chosen
tx_id string, optional The latest deposit's transaction id
overpaid boolean, optional true when more than 1% over amount_due_usd arrived
paid_late boolean, optional true when the payment was completed after it had expired or fallen short
deposits array of Deposit Every transfer to the payment, oldest first. [] until something arrives
expires_at string The customer's deadline: 15 minutes after the coin was set. Created without a coin, the customer has 1 hour to choose one, then 15 minutes to pay
created_at string When the payment was created
confirmed_at string, optional When the payment became final
network_fee_note string A short line for the customer about the network's fees and timing. "" until a coin is chosen
qr_payload string What to encode in a QR code: the deposit address alone (QR codes). "" until a coin is chosen
checkout_token string The public key to the payer's view of this payment (Checkout). Not included in webhooks
checkout_url string, optional The hosted checkout page for this payment. Absent when the gateway has no hosted checkout configured
token_name, network string, optional The coin and its network for people: "Tether", "Tron (TRC-20)". Absent until a coin is chosen
confirmations integer The latest deposit's confirmations when last checked; 0 before any deposit
confirmations_required integer How many make a deposit final on the payment's network (0 until a coin is chosen)
explorer_url string, optional tx_id on the network's block explorer. Absent for a test payment
detected_at string, optional When the first deposit was seen
opened_at string, optional When the hosted checkout was first opened
channel string Where the customer paid from: mobile or desktop (the hosted checkout, by browser), api (paid without opening it: your own page), "" while neither happened
link_id string, optional The payment link the payment was made through, when it was
customer_id string, optional The customer of your account that customer_email is filed under (the same for every payment to that email, in any case)
description string, optional As you sent it
metadata object, optional As you sent it
customer_email string, optional As you sent it
success_url string, optional As you sent it
cancel_url string, optional As you sent it
webhook_url string, optional As you sent it: this payment's notifications go there instead of the account's webhook URL (Webhooks)

The deposit object

Each entry of deposits is one transfer to the payment's address (or its destination tag on XRP):

Field Type Description
tx_id string The transaction id on its network. A simulated deposit's (simulated: true) looks like one but is on no chain
token string The asset that arrived. Usually the payment's token; another one for a wrong-asset deposit
chain string The network it arrived on
amount decimal string How much arrived, in token
usd_value decimal string Its dollar value: at rate_locked for the payment's own token, at the market price when it arrived for another asset
counted boolean Whether it counts toward the payment. Always true for the payment's own token; for another asset only with accept_other_assets on
confirmations integer How many confirmations it had when last checked; a final deposit shows at least confirmations_required
confirmations_required integer How many make it final on its network
explorer_url string, optional tx_id on the network's block explorer. Absent for a test payment's deposit
simulated boolean, optional true for a test payment's deposit, played by its simulation: no coins moved
late boolean, optional true when it arrived after the payment had closed
status string pending (confirming), confirmed (final; settled to you) or failed (never became final; nothing settled)
seen_at string When payitgo first saw it
confirmed_at string, optional When it became final
from string, optional The sender's address, when the network reports it (for Bitcoin, Litecoin and Dogecoin, the first input's). Where a refund would go
block integer, optional The block (on XRP, the ledger) that holds it
made_at string, optional That block's time
failed_at, fail_reason string, optional When and why it failed: transaction not found on chain or confirmation timeout

Every deposit that becomes final is settled to you (the parts of a payment paid in parts together, once it closes: one fee), counted or not, less the fees (Fees and settlement).

QR codes

qr_payload is the deposit address alone, on every network: the same as deposit_address. Show it as a QR code next to the address and the amount.

It carries no amount, token contract or XRP destination tag, on purpose: exchange apps such as Binance, where many customers pay from, reject payment URIs (ethereum:…, bitcoin:…, xrpl:…) as a malformed address. The customer types the amount, and for XRP the tag, so show both as text with copy buttons.

Amounts: what is asked, what is due, what arrived

Field In Meaning
usd_amount USD Your price, as you sent it in amount_usd (or price_amount converted at price_rate)
price_amount your currency Your price in price_currency, when you gave it in another currency than the dollar. Shown, never paid in: everything else is in dollars
amount_due_usd USD What the customer must pay: equal to usd_amount, unless you pass the fees on
rate_locked USD per coin The exchange rate, fixed for this payment when the coin was set
crypto_amount coin amount_due_usd ÷ rate_locked, rounded up: the exact amount to show the customer
received_amount coin The payment's own coin received so far
received_usd USD Everything that counts so far, in dollars
network_fee, network_fee_usd coin, USD The fee quoted for moving the money out of the deposit address, and for converting it when it is converted. It is taken from what arrives, and is not part of amount_due_usd unless you pass the fees on

For a BTC or ETH payment, read the dollars from amount_due_usd, received_usd and each deposit's usd_value; rate_locked is the dollar price of one coin. Settlements and payouts carry their dollar values too (Amounts in dollars).

A payment counts as paid when received_usd reaches amount_due_usd, less your underpayment tolerance.

With fees_paid_by_customer on, amount_due_usd is higher than usd_amount, so you receive your full price after the fees. A $100.00 order in USDT_TRC20 with a $1.39 network fee and the 1% service fee asks the customer for $102.42 (the formula).

Create a payment

POST /v1/payments · API key

Creates a payment for one of your orders. With a token, the payment gets its deposit address, locked rate and 15-minute deadline at once. Without one, the customer chooses the coin on the checkout page first.

Parameters

The body is JSON; send Content-Type: application/json (without it the body is not read, and you get order_id is required).

Field Type Required Description
order_id string yes Your id for the order, 1 to 128 bytes (spaces around it are trimmed). An order has at most one open payment per mode and per store: see safe retries
amount_usd number or decimal string yes, unless price_amount + price_currency The price in US dollars, more than 0: 49.90 or "49.90". It must reach the token's minimum
price_amount number or decimal string with price_currency Instead of amount_usd: the price in another currency, more than 0: 45.50 or "45.50". Converted into amount_usd (prices in other currencies)
price_currency string with price_amount The ISO 4217 code of price_amount's currency, such as EUR, GBP or NGN (case does not matter). USD works too
token string no The coin to pay with, from GET /v1/tokens, such as USDT_TRC20 (case does not matter). Leave it out to let the customer choose
tokens array of strings no Without token: the coins to offer the customer, such as ["USDT_TRC20", "USDT_BEP20"]. Default: every token you accept whose minimum the amount reaches. Ignored when token is set
description string no What the customer is paying for, up to 500 bytes. Shown on the hosted checkout
metadata object no Your own data: up to 20 keys, string values only. Keys up to 40 bytes, values up to 500
customer_email string no The customer's email address. Must be a valid address
success_url string no An http(s) URL the hosted checkout links to once the payment is confirmed
cancel_url string no An http(s) URL the hosted checkout links back to if the payment closes unpaid
webhook_url string no An https:// URL on a public host. This payment's notifications (its payment.* and settlement.* events) go here instead of your account's webhook URL, signed with the same secret. See Webhooks
simulate string no Test keys only: the outcome the payment plays, such as confirmed (outcomes). With token (or for expired) it starts at once; without, when the coin is chosen. wrong_asset needs token. A live key gets 400

Give the price as amount_usd, or as price_amount and price_currency together: not both. token and tokens may only name tokens you accept (accepted tokens).

Request

curl -s https://api.payitgo.co/v1/payments \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "order_id": "ORD-1001",
    "amount_usd": "49.90",
    "token": "USDT_TRC20",
    "description": "2 x Acme T-shirt",
    "metadata": {"customer_id": "cus_42"},
    "customer_email": "jane@example.com",
    "success_url": "https://acme.example/thanks",
    "cancel_url": "https://acme.example/cart"
  }'

Response

201 Created with the new payment (a test key, so livemode is false):

{
  "id": "8f3a2c1e9b7d4a6f05e1c2d3",
  "merchant_id": "3f8a1c27b94e05d6a2c71e90",
  "order_id": "ORD-1001",
  "status": "pending",
  "deposit_address": "TNVfZ8m3qR6xW2pL9kC4hJ7bD5sYt1uAeG",
  "token": "USDT_TRC20",
  "chain": "tron",
  "livemode": false,
  "crypto_amount": "49.9",
  "received_amount": "0",
  "usd_amount": "49.9",
  "amount_due_usd": "49.9",
  "fees_paid_by_customer": false,
  "network_fee": "3.212009",
  "network_fee_usd": "3.22",
  "settlement": "direct",
  "received_usd": "0",
  "rate_locked": "1",
  "deposits": [],
  "expires_at": "2026-10-10T09:15:00.112517888Z",
  "created_at": "2026-10-10T09:00:00.111798165Z",
  "network_fee_note": "Final in about a minute.",
  "qr_payload": "TNVfZ8m3qR6xW2pL9kC4hJ7bD5sYt1uAeG",
  "checkout_token": "6f1e2a9c4d7b3e05a8c1f2d4",
  "checkout_url": "https://pay.example.com/pay/6f1e2a9c4d7b3e05a8c1f2d4",
  "description": "2 x Acme T-shirt",
  "metadata": { "customer_id": "cus_42" },
  "customer_email": "jane@example.com",
  "success_url": "https://acme.example/thanks",
  "cancel_url": "https://acme.example/cart"
}

Send the customer to checkout_url, or show crypto_amount, token, deposit_address and expires_at on your own page (Checkout).

Let the customer choose the coin

Send only the order and the amount:

curl -s https://api.payitgo.co/v1/payments \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"order_id": "ORD-1002", "amount_usd": 25}'

201 Created. The payment has no coin and no address yet. token_options lists what the customer may choose from (shortened here to three of the twelve tokens), and expires_at is one hour away:

{
  "id": "c41e7a9d2b05f3e8a6d17c90",
  "merchant_id": "3f8a1c27b94e05d6a2c71e90",
  "order_id": "ORD-1002",
  "status": "pending",
  "token_options": [
    { "token": "USDT_TRC20", "chain": "tron", "min_usd": "6.44", "network_fee_usd": "3.22", "fee_note": "Final in about a minute." },
    { "token": "USDT_BEP20", "chain": "bsc", "min_usd": "1", "network_fee_usd": "0.01", "fee_note": "Final in under a minute." },
    { "token": "BTC", "chain": "bitcoin", "min_usd": "10", "network_fee_usd": "0.22", "fee_note": "Final in about 30 minutes (3 confirmations)." }
  ],
  "deposit_address": "",
  "token": "",
  "chain": "",
  "livemode": false,
  "crypto_amount": "0",
  "received_amount": "0",
  "usd_amount": "25",
  "amount_due_usd": "25",
  "fees_paid_by_customer": false,
  "network_fee": "0",
  "network_fee_usd": "0",
  "settlement": "direct",
  "received_usd": "0",
  "rate_locked": "0",
  "deposits": [],
  "expires_at": "2026-10-10T10:00:00.832686526Z",
  "created_at": "2026-10-10T09:00:00.832686526Z",
  "network_fee_note": "",
  "qr_payload": "",
  "checkout_token": "b28d4f61e9a03c75d2e8f1a6",
  "checkout_url": "https://pay.example.com/pay/b28d4f61e9a03c75d2e8f1a6"
}

The customer picks a coin on the hosted checkout, or on your own page: your server then sets it with POST /v1/payments/{id}/token. Only then does the payment get its deposit_address, rate_locked, crypto_amount, qr_payload and a new expires_at 15 minutes later. To offer fewer coins, pass "tokens": ["USDT_TRC20", "USDT_BEP20"].

With its own webhook URL

curl -s https://api.payitgo.co/v1/payments \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"order_id": "ORD-1003", "amount_usd": 30, "token": "USDT_TRC20", "webhook_url": "https://acme.example/orders/ORD-1003/ipn"}'

The response is the same payment object, with "webhook_url": "https://acme.example/orders/ORD-1003/ipn" at the end. Every payment.* and settlement.* notification of this payment goes to that URL, and none to your account's webhook URL.

Prices in other currencies

To price a payment in your own currency, send price_amount and price_currency (an ISO 4217 code: EUR, GBP, NGN…) instead of amount_usd:

curl -s https://api.payitgo.co/v1/payments \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"order_id": "ORD-1004", "price_amount": "45.50", "price_currency": "EUR", "token": "USDT_TRC20"}'

payitgo converts the price into dollars when the payment is created, at the exchange rate of the moment, and rounds it to the cent. The response is the usual payment object, priced in dollars, with your price and the rate next to it:

{
  "usd_amount": "49.23",
  "price_amount": "45.5",
  "price_currency": "EUR",
  "price_rate": "1.082",
  "amount_due_usd": "49.23",
  "crypto_amount": "49.23",
  "token": "USDT_TRC20"
}

(Shortened: every other field is there as usual.)

Safe retries

While an order has an open payment (pending, partially_paid or detecting) in the key's mode, creating it again returns that same payment with 200 OK instead of 201, and makes nothing new. The retried request is still checked for errors, but its other fields (amount, token, webhook_url…) are ignored: the customer is never shown a second address or amount. So a request that timed out can simply be sent again.

Once the payment is closed (confirmed, expired, underpaid or failed), the same order_id creates a new payment. Test and live payments never share an order.

Order ids are unique per store. A store's key only finds its own store's open payments: two shops on one account can both send "order_id": "1234" and get two payments, as long as each uses the key of its own store. A key with no store sees the payments made with keys that have no store. So you never change your order numbers to keep shops apart: give each shop its own store and key.

Errors

Every error is {"error": "<message>"}.

Status Message Cause
400 invalid request body The body is not JSON, or a field has the wrong type (amount_usd or price_amount not a number, a metadata value that is not a string)
400 order_id is required Missing or empty order_id (or no Content-Type: application/json)
400 order_id is too long More than 128 bytes
400 amount_usd must be > 0 Zero, negative or missing amount (neither amount_usd nor price_amount + price_currency)
400 give amount_usd, or price_amount and price_currency: not both amount_usd together with a price in another currency
400 price_amount and price_currency go together Only one of the two
400 price_currency must be the ISO 4217 code of a currency (EUR, GBP, NGN…) price_currency is not a currency in use, such as BTC or EURO
400 price_amount must be > 0 Zero or negative price_amount
400 price_amount is worth less than a cent price_amount converts to $0.00 once rounded to the cent
400 unsupported token in test mode token is not a token of the key's mode (live mode with a live key)
400 unsupported token in test mode: SHIB An entry of tokens is not a token of the key's mode
400 not enabled for your account: BTC (turn tokens on in your payment settings) A token you have not enabled (accepted tokens)
400 minimum payment in USDT_TRC20 is $6.44 right now (network fees) amount_usd is under the token's current minimum
400 amount_usd is below the minimum of every token on offer Without token: no coin on offer accepts so small an amount
400 none of the tokens on offer is available in test mode Without token: none of tokens is offered in this mode
400 only test payments can be simulated: use a test key simulate with a live key
400 simulate must be one of: confirmed, overpaid, … An unknown simulate outcome
400 simulate wrong_asset needs the payment's coin: pass token "simulate": "wrong_asset" without token
400 a BTC address only receives BTC: wrong_asset needs a coin on Tron, Ethereum or BNB Smart Chain wrong_asset with a coin that has no other on its network
400 description is too long More than 500 bytes
400 metadata may hold at most 20 keys Too many keys
400 metadata key or value is too long A key over 40 bytes or a value over 500
400 invalid customer_email Not an email address
403 this customer is blocked from paying the merchant You blocked the customer with this customer_email in the dashboard
400 success_url and cancel_url must be http(s) URLs Not an absolute http:// or https:// URL
400 webhook_url: url must use https webhook_url is http://
400 webhook_url: url must be an absolute http(s) URL Not an absolute URL
400 webhook_url: url must point at a public host Its host is localhost, a private or link-local address
400 webhook_url: url host does not resolve Its host name has no DNS record
401 missing api key · invalid api key See Authentication
403 live mode is off for this account: … A live key before live mode is on
403 this api key is read-only: it can only read (GET) A read-only key
429 rate limit exceeded Too many requests (limits)
500 payment creation failed Something failed on payitgo's side. Retry with the same order_id: it is safe
503 exchange rate unavailable for price_currency: try again shortly No exchange rate for price_currency could be had (prices in other currencies). Nothing was created: retry shortly with the same order_id

Choose the coin later

POST /v1/payments/{id}/token · API key

Sets the coin of a payment created without one, once the customer has picked it on your own page. The answer is the whole payment, as GET /v1/payments/{id} returns it, now with its address, locked rate, amount, QR code payload and a deadline 15 minutes away: everything your page shows. SDKs: payments.selectToken(id, token) (select_token in Python, SelectToken in Go).

Parameter In Type Required Description
id path string yes The payment's id or your order_id
token body string yes One of the payment's token_options[].token, such as USDT_TRC20
curl -s https://api.payitgo.co/v1/payments/c41e7a9d2b05f3e8a6d17c90/token \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"token": "USDT_TRC20"}'

200 OK:

{
  "id": "c41e7a9d2b05f3e8a6d17c90",
  "merchant_id": "3f8a1c27b94e05d6a2c71e90",
  "order_id": "ORD-1002",
  "status": "pending",
  "deposit_address": "TJr7Wq2nX5kP8sB3vH6mC9dF4gL1tYzRaU",
  "token": "USDT_TRC20",
  "chain": "tron",
  "livemode": false,
  "crypto_amount": "25",
  "received_amount": "0",
  "usd_amount": "25",
  "amount_due_usd": "25",
  "fees_paid_by_customer": false,
  "network_fee": "3.211899",
  "network_fee_usd": "3.22",
  "settlement": "direct",
  "received_usd": "0",
  "rate_locked": "1",
  "deposits": [],
  "expires_at": "2026-10-10T09:16:48.029135472Z",
  "created_at": "2026-10-10T09:00:00.832686526Z",
  "network_fee_note": "Final in about a minute.",
  "qr_payload": "TJr7Wq2nX5kP8sB3vH6mC9dF4gL1tYzRaU",
  "checkout_token": "b28d4f61e9a03c75d2e8f1a6",
  "checkout_url": "https://pay.example.com/pay/b28d4f61e9a03c75d2e8f1a6"
}

The coin can be chosen once, by your server or on the hosted checkout, whichever comes first.

Status Message Cause
400 token is required No token in the body, or the body is not JSON
400 token is not one of this payment's options A coin not in token_options
400 minimum payment in … is $… right now (it is converted on its way to the merchant, and the exchange takes no less) The coin needs a bigger payment than this one: it is converted through the exchange on its way to the merchant, which takes no less, and the merchant pays the fees or hides such coins (hide_below_minimum). Offer the other coins
400 this payment can no longer be paid The payment is no longer pending, or its hour to choose has passed
404 payment not found No such payment for your key's mode (or, for a storefront's key, for its store)
409 the coin for this payment has already been chosen The coin was set before

Simulate a test payment

POST /v1/payments/{id}/simulate · API key (test keys only)

A test payment moves no coins. Choose what happens to it instead: payitgo plays the deposits, status changes and webhooks a few seconds apart, through the same processing as live coins. Every outcome, the webhooks it sends and the rules: Test mode.

Parameter In Type Required Description
id path string yes The payment's id or your order_id
outcome body string yes confirmed, overpaid, partially_paid, underpaid, expired, failed, wrong_asset or paid_late
curl -s -X POST https://api.payitgo.co/v1/payments/c41e7a9d2b05f3e8a6d17c90/simulate \
  -H "Authorization: Bearer $PAYITGO_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"outcome": "partially_paid"}'

200 OK with the payment, its simulation started:

{
  "id": "c41e7a9d2b05f3e8a6d17c90",
  "order_id": "ORD-1002",
  "status": "pending",
  "token": "USDT_TRC20",
  "livemode": false,
  "simulation": { "outcome": "partially_paid", "state": "running" },
  "…": "…"
}

The payment must be pending or partially_paid, and have its coin (except for expired). One simulation runs at a time; once it is done, you can run another (confirmed after partially_paid, to pay the rest). To choose the outcome when you create the payment, pass simulate to POST /v1/payments.

Status Message Cause
400 outcome is required No outcome in the body, or the body is not JSON
400 simulate must be one of: confirmed, overpaid, … An unknown outcome
400 only test payments can be simulated: use a test key A live key
400 choose the payment's coin first No coin yet, for anything but expired
400 this payment is confirmed: only a pending or partially paid payment can be simulated The payment is closed or confirming
400 a BTC address only receives BTC: wrong_asset needs a coin on Tron, Ethereum or BNB Smart Chain wrong_asset on a coin that has no other on its network
404 payment not found No such payment for your key's mode (or, for a storefront's key, for its store)
409 a simulation is already running for this payment Wait for its done

Retrieve a payment

GET /v1/payments/{id} · API key

Returns one payment of the key's mode.

Parameter In Description
id path The payment's id, or your order_id. The payment id is tried first, then your order id: by order id you get the order's open payment of the key's store (or of no store, for a key with none); a closed one is found by its payment id
curl -s https://api.payitgo.co/v1/payments/8f3a2c1e9b7d4a6f05e1c2d3 \
  -H "Authorization: Bearer $PAYITGO_KEY"
curl -s https://api.payitgo.co/v1/payments/ORD-1001 \
  -H "Authorization: Bearer $PAYITGO_KEY"

200 OK with the payment object.

Status Message Cause
404 payment not found No such payment for your account in the key's mode, an order id with no open payment, or (with a storefront key) another store's payment

Webhooks tell you about every change, so you rarely need to poll. When you do (after your server was down, or to double-check a webhook), fetch the payment from your server and trust what it says over the browser.

List payments

GET /v1/payments · API key

Your payments in the key's mode, newest first. An account-wide key sees all of the account's payments; a storefront key sees only its own store's.

Query parameter Type Default Description
status string all One status, such as confirmed, or several separated by commas: pending,partially_paid,detecting
token string all Only payments in this token, such as USDT_TRC20
chain string all Only payments on this network, such as tron
store_id string all Only payments created by this store's keys. Ignored for a storefront key, which always gets its own store's
customer_id string all Only one customer's payments (from a payment's customer_id)
link_id string all Only the payments made through one payment link
q string Search: a payment id, order id, transaction hash or deposit address (exactly), or any part of the customer email or description (any case)
from, to string Only payments created from from (included) to to (excluded): RFC 3339 times or YYYY-MM-DD dates (midnight UTC). Anything else is 400
limit integer 50 How many to return, 1 to 200. A value outside that range is replaced by 50
offset integer 0 How many to skip, for paging. A negative value is replaced by 0
curl -s 'https://api.payitgo.co/v1/payments?status=pending&limit=2&offset=0' \
  -H "Authorization: Bearer $PAYITGO_KEY"

200 OK:

{
  "payments": [
    { "id": "2d9b6e14a7c3f0851e4b9a6c", "order_id": "ORD-1003", "status": "pending", "…": "…" },
    { "id": "c41e7a9d2b05f3e8a6d17c90", "order_id": "ORD-1002", "status": "pending", "…": "…" }
  ],
  "total": 3,
  "limit": 2,
  "offset": 0
}
Field Type Description
payments array of payments This page, in full (shortened above)
total integer How many payments match the filters, across all pages
limit, offset integer The paging applied: what you asked for, or the defaults that replaced an out-of-range value

To page through everything, repeat with offset increased by limit until offset reaches total. An unknown status, token or chain simply matches nothing ("payments": []).

List tokens

GET /v1/tokens · API key · SDKs: tokens.list()

The coins your customers can pay with in the key's mode: the tokens you accept (accepted tokens), each with its network fee and minimum right now. Use it to show the coins before you create a payment, then create the payment with the customer's choice as its token.

Parameter In Type Required Description
amount_usd query decimal no The order's amount. Only the tokens a payment of that amount offers are listed (the ones whose min_usd it reaches), each with amount_due_usd when you pass the fees on: exactly the token_options a payment of that amount would have
curl -s https://api.payitgo.co/v1/tokens -H "Authorization: Bearer $PAYITGO_KEY"

200 OK (test mode, shortened to four of the twelve tokens):

{
  "tokens": [
    { "token": "USDT_TRC20", "chain": "tron", "min_usd": "6.44", "network_fee_usd": "3.22", "fee_note": "Final in about a minute." },
    { "token": "TRX", "chain": "tron", "min_usd": "1", "network_fee_usd": "0", "fee_note": "Final in about a minute." },
    { "token": "USDT_BEP20", "chain": "bsc", "min_usd": "1", "network_fee_usd": "0.01", "fee_note": "Final in under a minute." },
    { "token": "XRP", "chain": "xrp", "min_usd": "1", "network_fee_usd": "0.01", "fee_note": "Final in seconds. Include the destination tag: it is required." }
  ]
}

With an amount, and the fees passed on to the customer, each token says what the customer pays in it:

curl -s 'https://api.payitgo.co/v1/tokens?amount_usd=40' -H "Authorization: Bearer $PAYITGO_KEY"
{
  "tokens": [
    { "token": "USDT_TRC20", "chain": "tron", "min_usd": "6.44", "network_fee_usd": "3.22", "amount_due_usd": "43.66", "fee_note": "Final in about a minute." },
    { "token": "USDT_BEP20", "chain": "bsc", "min_usd": "1", "network_fee_usd": "0.01", "amount_due_usd": "40.42", "fee_note": "Final in under a minute." }
  ]
}
Status Message Cause
400 amount_usd must be > 0 amount_usd is not a positive number

The TokenInfo object

Field Type Description
token string The token symbol to use in token and tokens
chain string Its network
name, network string For people: "USD Coin", "Ethereum (ERC-20)"
confirmations integer How many confirmations make a deposit in it final
min_usd decimal string The smallest amount_usd accepted in this token right now: the token's own minimum; twice its network fee when you pay the fees; and, for a coin converted across networks on its way to your wallet, the price that leaves the exchange its minimum, when you pay the fees or turn on hide_below_minimum (with the fees passed on, a smaller payment pays up to the minimum instead: extra_usd), whichever is highest
network_fee_usd decimal string The network fee for moving a payment in this token, right now: its fixed part, whatever the amount. It rises and falls with how busy the network is
network_fee_bps integer, optional For a coin that is converted (into your payout coin, or bought by payitgo for your balance): the share of the payment the network fee adds on top, in basis points (180: 1.8%), what the exchange keeps and the conversion fee. Absent when nothing is converted
amount_due_usd decimal string, optional In a payment's token_options, and in GET /v1/tokens?amount_usd=…, when you pass the fees on: what the customer pays in this coin, every fee and the conversion included, so they can pick the cheapest
extra_usd decimal string, optional What of amount_due_usd the customer pays only to reach the exchange's minimum, for a payment too small for this coin's conversion: you receive it. Absent when none
fee_note string A short line for the customer about fees and timing

Fees and minimums are quoted for where a payment in the coin will really settle: with direct settlement and a payout address for the coin (in use, or still in its 24 hours: the payment then waits for it), two transfers (to you, and payitgo's fee); with none for the coin, the conversion into one of your payout coins as well (coins you have no payout address for); in your balance, one transfer, and for any coin but USDT on Tron the 1.5% conversion fee. GET /v1/tokens, the minimum checked by POST /v1/payments and a payment's token_options always agree. See Fees and settlement.

When the customer pays the wrong amount

Too little (say 30 of 49.90 USDT). The payment becomes partially_paid and the checkout page asks for the rest. If the rest arrives before expires_at, the payment goes on to detecting and confirmed. If not, it becomes underpaid at the deadline: the 30 USDT are still settled to you (less the fees) once final, so you can refund the customer or agree on the rest. Nothing is left on the deposit address.

A little too little. A payment counts as paid when the shortfall is within your underpayment tolerance: 0.5% by default, so a wallet that rounds or takes a tiny fee does not leave the customer stuck. Owners can set it from 0 to 20% (PATCH /merchant/settings {"underpaid_tolerance_bps": 100} for 1%). A payment keeps the tolerance in force when it was created.

Too much (say 900 of 100 USDT). The payment confirms with "overpaid": true (set when more than 1% too much arrived), and all of it is settled to you. Refund the difference if you wish: the sender's address is in the deposit's from.

Twice, or late. See late payments.

When the customer pays with the wrong asset

A payment's address can only receive coins of its own network: a wallet will not send Bitcoin to a Tron address. The mistakes that do happen are on the same network, and payitgo catches them:

Each such deposit raises payment.wrong_asset. What happens next depends on the accept other assets setting (PATCH /merchant/settings {"accept_other_assets": true}, owner only):

Setting The deposit The payment
Off (default) "counted": false. Settled to you in the asset that arrived Keeps waiting for the coin it asked for
On "counted": true, at its dollar value (stablecoins at $1, others at the market price when it arrives), if you accept that token Completes when the dollar total is reached

Either way the money is yours to keep or refund. It is settled like any payment in the coin that arrived: with direct settlement, to your payout address for that coin, or converted into one of your payout coins when you have none for it (coins you have no payout address for).

Late payments

A payment's address is watched for 24 hours after it expires, falls short or confirms. A deposit in that time is marked "late": true, settled to you once final, and reported with payment.late_deposit. If it completes an expired or underpaid payment, the payment moves on to detecting and confirmed with "paid_late": true, at the rate locked at creation. A late deposit that is still not enough makes the payment underpaid. A deposit after a payment confirmed (a customer paying twice) marks it overpaid.

XRP

XRP payments share one deposit address and are told apart by destination_tag. The customer must include the tag: a transfer without it cannot be matched to the payment and needs payitgo's support to recover. The hosted checkout shows the tag next to the address; qr_payload does not carry it, so the customer types it in.

Without a library

The libraries wrap these calls. In plain code:

Node.js

const res = await fetch("https://api.payitgo.co/v1/payments", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PAYITGO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ order_id: "ORD-1001", amount_usd: "49.90", token: "USDT_TRC20" }),
});
const body = await res.json();
if (!res.ok) throw new Error(`payitgo ${res.status}: ${body.error}`);
// res.status is 201 (created) or 200 (the order's open payment)
const payment = body; // redirect the customer to payment.checkout_url

Python

import os, requests

r = requests.post(
    "https://api.payitgo.co/v1/payments",
    headers={"Authorization": f"Bearer {os.environ['PAYITGO_KEY']}"},
    json={"order_id": "ORD-1001", "amount_usd": "49.90", "token": "USDT_TRC20"},
    timeout=30,
)
if r.status_code >= 400:
    raise RuntimeError(f"payitgo {r.status_code}: {r.json()['error']}")
payment = r.json()

PHP

$ch = curl_init('https://api.payitgo.co/v1/payments');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PAYITGO_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'order_id' => 'ORD-1001', 'amount_usd' => '49.90', 'token' => 'USDT_TRC20',
    ]),
]);
$body = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
    throw new RuntimeException('payitgo: ' . $body['error']);
}
$payment = $body;

Go

body, _ := json.Marshal(map[string]any{"order_id": "ORD-1001", "amount_usd": "49.90", "token": "USDT_TRC20"})
req, _ := http.NewRequest(http.MethodPost, "https://api.payitgo.co/v1/payments", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAYITGO_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
var payment map[string]any
json.NewDecoder(resp.Body).Decode(&payment)
if resp.StatusCode >= 400 {
	return fmt.Errorf("payitgo %d: %v", resp.StatusCode, payment["error"])
}