Create Invoice

POST /api/v1/invoices

FieldTypeRequiredNotes
amountstringyesDecimal string, e.g. "19.99". Never send floats.
currencystringnoUSD (default), EUR, GEL
assetstringnoUSDT (default)
networkstringnoTRON (default)
external_idstringnoYour order id. Unique per merchant.
descriptionstringnoShown on checkout
customerobjectno{email, external_id, name, metadata}
expires_inintegernoSeconds, 300–86400 (default 1800)
metadataobjectnoReturned unchanged in API and webhooks
success_url / cancel_urlstringnoWhere checkout sends the customer afterwards
webhook_urlstringnoExtra per-invoice webhook target (signed with your merchant secret)
payment_address_idstringnoUse a specific registered address instead of the default
payment_modestringnoONE_PAYMENT_REQUIRED (default) or AGGREGATE_PAYMENTS
underpayment_tolerance_percentstringnoe.g. "0.5"; default 0 = exact payment

Pricing

Fiat is converted with the exchange rate valid at creation and frozen on the invoice (exchange_rate.rate, source, timestamp). The USDT amount is rounded up to 2 decimals so you never receive less than the fiat price.

When another open invoice on the same address already expects the same amount, a tiny unique offset is added (e.g. 19.9901) so on-chain matching stays unambiguous. Always show the customer crypto_amount, never recompute it.

Idempotency

Send Idempotency-Key: <unique per order> on every POST. Repeating the request returns the original response (with Idempotency-Replayed: true). Reusing a key with a different body returns 422 idempotency_key_reused. Keys expire after 24 hours.

Other endpoints

GET  /api/v1/invoices?status=paid,pending&external_id=…&created_from=…&per_page=25
GET  /api/v1/invoices/{id}
POST /api/v1/invoices/{id}/cancel        (only while pending)

Example with PHP:

$response = Http::withToken($secret)
    ->withHeaders(['Idempotency-Key' => 'order-'.$order->id])
    ->post('https://api.usdtpay.ge/api/v1/invoices', [
        'amount' => (string) $order->total,
        'currency' => 'USD',
        'external_id' => 'ORDER-'.$order->id,
        'success_url' => route('orders.thanks', $order),
    ])->throw()->json();

return redirect()->away($response['checkout_url']);

Node.js:

const res = await fetch('https://api.usdtpay.ge/api/v1/invoices', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.GATEWAY_SECRET}`, 'Idempotency-Key': `order-${order.id}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ amount: order.total.toFixed(2), currency: 'USD', external_id: `ORDER-${order.id}` }),
});
const invoice = await res.json();

Python:

import requests
invoice = requests.post(
    "https://api.usdtpay.ge/api/v1/invoices",
    headers={"Authorization": f"Bearer {SECRET}", "Idempotency-Key": f"order-{order.id}"},
    json={"amount": str(order.total), "currency": "USD", "external_id": f"ORDER-{order.id}"},
    timeout=15,
).json()