FastPay Docs
Sign in Create account

FastPay API documentation

Accept crypto on your website or Telegram bot: ten currencies across seven networks, prices always in USD, one hosted checkout and one signed webhook — plus a no-code path with payment links. All requests and responses are JSON over HTTPS.

Base URLhttps://fast-pay.money
AuthenticationAuthorization: Bearer YOUR_API_KEY
FormatContent-Type: application/json

Getting API keys

  1. Create a free account — only an email is needed.
  2. Open Business → + New merchant and connect your website or Telegram bot.
  3. Websites confirm domain ownership with a DNS TXT record, a meta tag or a file — any one of the three.
  4. After a quick review your merchant becomes Active and the API key starts working.

The fpk_… key is shown once at creation. Store it on your server — never expose it in browser code. A lost key can be regenerated in the merchant card at any time; the previous key is revoked immediately. The webhook secret for signature checks lives in the same card.

Request format

Send the API key in the Authorization header on every request. Only merchants in the Active status can call the API.

Authorization: Bearer fpk_your_api_key
Content-Type: application/json

Successful responses return HTTP 200 with a JSON body. Errors return a matching 4xx/5xx status and a JSON object with a single error field — see Errors & limits.

Payments — getting started

The flow is the same for a website and a bot:

  1. Create an invoice from your backend when the customer checks out.
  2. Redirect the customer to the returned url — the hosted FastPay checkout.
  3. The customer picks a currency (USDT, USDC, DAI, BNB, ETH, SOL, TON, TRX, POL, AVAX) and network, then sends the exact amount shown on the checkout.
  4. We detect the transfer on-chain, credit your Business balance minus the service fee and mark the invoice paid.
  5. Your server gets a signed webhook — or you poll the invoice status.

Set a Support URL in your merchant card: a customer whose payment fell short sees it on the checkout together with the payment ID, so they can reach you instead of guessing.

Who pays the service fee is your choice per merchant — My business (deducted from the credit) or Customer (added on top at checkout). Switch it in the merchant card.

Create an invoice

POST/api/v1/invoice
FieldType
amountnumberrequired Invoice amount in USD, 0.01 – 100 000.
order_idstringOptional. Your order reference, up to 64 characters. Returned in the webhook.
descriptionstringOptional. Shown to the customer on the checkout, up to 200 chars.
return_urlstringOptional. Where the customer goes after a successful payment.
underpaid_tolerance_percentnumberOptional, 0–5. A payment short of the invoice amount by at most this percentage still settles the invoice — useful when customers pay from exchanges that deduct their own fee. If omitted, the merchant's own setting applies (Business → merchant card), and failing that the platform default.
testbooleanOptional. Creates a test invoice: the checkout offers a simulated payment and the webhook carries test: true; no funds move.
curl -X POST https://fast-pay.money/api/v1/invoice \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 20,
    "order_id": "ORDER-1001",
    "description": "Premium plan",
    "return_url": "https://yourstore.com/thanks"
  }'
Response
{
  "invoice": {
    "uuid": "6e249771-4ced-4a9d-9c67-2efb1876070f",
    "url": "https://fast-pay.money/pay/6e249771-4ced-4a9d-9c67-2efb1876070f",
    "status": "pending",
    "amount": "20.00",
    "currency": "USD",
    "order_id": "ORDER-1001",
    "expires_at": "2026-07-25 05:08:16",
    "created_at": "2026-07-25 04:08:16"
  }
}

Redirect your customer to url. The invoice stays payable for 60 minutes by default; the exact deadline is always in expires_at.

Invoice information

GET/api/v1/invoice?uuid=<uuid>
curl "https://fast-pay.money/api/v1/invoice?uuid=6e249771-4ced-4a9d-9c67-2efb1876070f" \
  -H "Authorization: Bearer YOUR_API_KEY"
Paid invoice — fields beyond the pending shape
{
  "invoice": {
    "status": "paid",
    "asset": "USDT",
    "network": "bep20",
    "payment_amount": "20.00",
    "paid_amount": "20.00",
    "fee": "0.10",
    "tx_hash": "0xc267…a02c",
    "paid_at": "2026-07-25 04:12:31"
  }
}

Amount formats: stablecoins are quoted with 2 decimals, other coins with 8. tx_hash is null for balance-detected coins.

Payment statuses

Status
pendingWaiting for the customer to pay. The checkout is open.
paidPayment detected and credited to your Business balance.
underpaidPart of the amount arrived. The invoice stays open, the payer is shown what is missing, and the funds are already credited to your Business balance. Shown in the dashboard and carried by the underpaid webhook. When polling the API, watch payment_status (wrong_amount_waiting) — the plain status field only ever holds pending, paid or expired, and the same value is mirrored in raw_status.
expiredThe payment window closed without a payment.
Machine-readable status

Alongside status, every invoice and every webhook carries payment_status — a detailed code meant for automated handling. It distinguishes cases that status merges:

payment_statusMeaningCredit the customer?
checkWaiting for the paymentno
paidExact amount receivedyes
paid_overMore than required receivedyes — see paid_amount
wrong_amount_waitingShort payment, the invoice is still open for a top-upno — hold the order
wrong_amountShort payment, the window has closedno — resolve manually
cancelNothing paid, the window has closedno
refund_process / refund_paidRefund requested / sentreverse the order

Credit only on paid and paid_over. An underpayment never produces a paid event, so a platform that cannot credit partial amounts needs no special handling: it simply ignores everything else and waits for the customer's top-up, which arrives as a normal paid event for the same uuid.

A transfer that arrives with a wrong amount, in a different coin or after the grace window is still credited to your Business balance as a plain deposit — customer funds are never dropped. See Underpayments & edge cases for the exact rules.

Underpayments & edge cases

Real customers pay from exchanges and custodial wallets that may deduct their own fee from the sum, send late or pick the wrong coin. The rules below are deterministic; no payment is ever lost.

ScenarioWhat happens
Exact amount receivedInvoice settles as paid; the webhook fires.
OverpaymentInvoice settles as paid; the full received amount is credited to you and reported in paid_amount.
Underpayment within underpaid_tolerance_percentInvoice settles as paid with the actually received amount. Set the tolerance per invoice at creation (0–5%).
Underpayment beyond the toleranceThe transfer is attributed to the invoice, credited to your Business balance and reported as underpaid with an underpaid webhook. The invoice stays open and the checkout tells the payer exactly how much is missing, shows the transaction hash, the payment ID and your support link.
The customer sends the rest laterTransfers accumulate on the invoice. As soon as the total reaches the amount due (minus the tolerance), the invoice settles as paid and the paid webhook is delivered with the full paid_amount.
Payment after expiry, within 30 minutesStill settles the invoice — a transfer broadcast in time but confirmed late is honoured (grace window). The expired webhook is only sent after this window closes.
Payment after the grace windowCredited to your Business balance as a plain deposit; the invoice remains expired.
A different supported coin sent to the payment addressCredited to your Business balance as a plain deposit; the invoice is not affected.

Plain deposits are visible in your Business transaction history with the sending transaction hash, so any disputed payment can be traced on-chain.

Webhooks

Set the Webhook URL in your merchant card. We POST a JSON event when an invoice is paid, when a payment arrives but falls short (status: "underpaid") and when the invoice expires unpaid (status: "expired", sent after the 30-minute late-payment grace window closes). All three share one shape. A paid event looks like this:

{
  "type": "payment",
  "uuid": "6e249771-4ced-4a9d-9c67-2efb1876070f",
  "order_id": "ORDER-1001",
  "status": "paid",
  "amount": "20.00",
  "payment_amount": "20.0000",
  "paid_amount": "20.0000",
  "asset": "USDT",
  "network": "bep20",
  "tx_hash": "0xc267…a02c",
  "fee": "0.1000",
  "test": false,
  "merchant_id": "21aef00c-6ccf-4f7b-a623-4668ce5af5ac",
  "paid_at": "2026-07-25 04:12:31"
}

Every delivery is signed: the X-FastPay-Signature header is the hex HMAC-SHA256 of the raw request body, keyed with your webhook secret. Always verify it:

// Node.js
const crypto = require('crypto');

function isValidWebhook(rawBody, signatureHeader, webhookSecret) {
  const expected = crypto.createHmac('sha256', webhookSecret)
    .update(rawBody).digest('hex');
  const got = Buffer.from(signatureHeader || '', 'hex');
  const want = Buffer.from(expected, 'hex');
  // Lengths must match before comparing — timingSafeEqual throws otherwise,
  // and a forged or missing signature is exactly the mismatched case.
  if (got.length !== want.length) return false;
  return crypto.timingSafeEqual(got, want);
}
<?php // PHP
function is_valid_webhook(string $rawBody, string $signature, string $secret): bool {
    return hash_equals(hash_hmac('sha256', $rawBody, $secret), $signature);
}

received_amount is the total received for this invoice so far and remaining_amount is what is still missing — both are present on every event, which is all you need to handle an underpaid notification: hold the order, tell the customer, or credit them partially. A later top-up produces a paid event for the same uuid. Both figures are in the coin the customer is paying with; the same two values in USD arrive as received_amount_usd and remaining_amount_usd, converted at the rate locked when the customer picked the currency — so a short payment in LTC or BTC can be read in dollars without fetching a rate yourself. They are null until a currency is picked.

amount is the invoice face value in USD; payment_amount, paid_amount and fee are in the currency from asset. tx_hash can be null for coins settled by balance detection (BNB, ETH, POL, AVAX, SOL) — do not require it.

Respond with any 2xx to acknowledge. If your endpoint is down we retry with growing delays from a durable queue, and you can resend any webhook manually from the payment card in the dashboard. Deliveries can arrive more than once — treat the invoice uuid as your idempotency key.

Testing

Pass "test": true when creating an invoice to get a test invoice: the checkout shows a TEST MODE banner and a «Simulate payment» button instead of waiting for a real transfer. Pressing it flips the invoice to paid and delivers a normal signed webhook with "test": true — no money and no balances move anywhere.

curl -X POST https://fast-pay.money/api/v1/invoice \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 5, "order_id": "TEST-1", "test": true }'

To verify webhook delivery before your own endpoint is ready, set the merchant's Webhook URL to https://fast-pay.money/webhook-echo: it accepts the POST, answers 200 and stores nothing. The delivery status is visible on the payment card in the dashboard.

Always check the test field in your webhook handler and skip crediting when it is true. Test invoices are excluded from your income stats. For an end-to-end rehearsal with real money, any live invoice from $0.50 works — or try the flow yourself at the demo shop.

Playground

Try the API right here. Paste your API key (Business → merchant card), create a test invoice and walk the whole flow — the «Simulate payment» button on the checkout completes it and delivers your webhook with test: true. The key stays in your browser only.

Refunds

POST/api/v1/invoice/refund
FieldType
uuidstringRequired. The paid invoice to refund.
addressstringOptional. Defaults to the payer address we captured on-chain; required only when it could not be detected.
curl -X POST https://fast-pay.money/api/v1/invoice/refund \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "uuid": "6e249771-4ced-4a9d-9c67-2efb1876070f" }'
Response
{
  "refund": { "withdrawal_id": "b0c1…", "amount": "19.54", "fee": "0.50", "status": "requested" }
}

The amount you were credited for the invoice is debited from your Business balance; the payer receives it minus the network payout fee. Refunds go out after a quick review, one refund per invoice. Currently available for USDT payments on TRC-20, BEP-20 and Polygon; the same button lives on the payment card in Business → Payments. refund_status on the invoice moves requested → sent; a rejected refund returns the money to your balance and lets you retry.

Payouts

Withdraw USDT from your balance to any external address on TRON (TRC-20), BSC (BEP-20) or Polygon: dashboard → Personal → Send. Business income moves to Personal with the free internal transfer first.

  1. Enter the address — the network is detected from its format.
  2. Review the per-network fee and confirm. The amount plus the fee is reserved from your balance.
  3. Every payout is individually reviewed, then sent on-chain. You get notifications at each step, and the tx hash appears in your request list.
  4. If a payout is rejected or fails, the full reserved amount returns to your balance automatically.

Current limits and per-network fees are shown right in the Send form.

Supported currencies & networks

Invoices are always created in USD. On the checkout the customer picks what to pay with; you are credited in the currency they paid.

CurrencyNetworks
USDTTRC-20, BEP-20, ERC-20, Polygon, Avalanche
USDCBEP-20, ERC-20, Polygon, Avalanche
DAIBEP-20, ERC-20, Polygon
BNB / ETH / POL / AVAXnative coin of its chain
TRXTRON
SOLSolana
TON (GRAM in the API)TON

There is no business minimum — invoices are accepted from the $0.01 accounting floor; per-shop minimums are your own policy. * Non-stablecoin rates come from the live market and are locked for the invoice lifetime (60 minutes by default, see expires_at). Coin amounts are calculated to eight decimals, the precision the chains themselves use, and the exact figure is shown to the customer before they pay. The wallet holds 16 coins across 9 chains for deposits and storage; BTC and LTC on the checkout are next.

Exchange rates

GET/api/v1/exchange-rates

Returns indicative USD market rates for every supported currency. Stablecoins are pinned to 1. The rate applied to an invoice is locked at the moment the customer picks the currency and held for the invoice lifetime; this endpoint is for display purposes on your side.

curl "https://fast-pay.money/api/v1/exchange-rates" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "base": "USD",
  "rates": {
    "USDT": "1.00000000",
    "USDC": "1.00000000",
    "DAI": "1.00000000",
    "BNB": "567.33000000",
    "ETH": "1869.17000000",
    "POL": "0.08000000",
    "AVAX": "6.54000000",
    "TRX": "0.33000000",
    "SOL": "74.20000000",
    "GRAM": "1.60000000"
  }
}

Errors & limits

Every error is returned as JSON with an HTTP status and a single human-readable error field:

{ "error": "amount must be between 0.01 and 100000 USD" }
CodeMeaning
400Invalid input — the message names the field or the rule.
401Missing or invalid API key, or the merchant is not active.
404Invoice not found or it belongs to another merchant.
429Rate limit exceeded — retry with a backoff.
5xxTemporary server issue — retry with a backoff.
Error messages by endpoint
EndpointHTTPMessage
any401Missing or malformed API key
401Invalid API key — also returned while the merchant is not Active.
429Too many attempts, try again later
POST /invoice400amount must be a number
400amount must be between 0.01 and 100000 USD
400return_url must be a valid http(s) URL
400underpaid_tolerance_percent must be a number between 0 and 5
GET /invoice404Invoice not found
POST /invoice/refund404Invoice not found
400Only paid invoices can be refunded
400Test invoices cannot be refunded
400Refunds are currently available for USDT payments only — contact support for other coins
400Provide a valid payer address for the refund
400A refund for this invoice already exists
400Insufficient Business balance for the refund
Rate limits, per IP address
EndpointRequests per minute
POST /api/v1/invoice120
GET /api/v1/invoice300
GET /api/v1/exchange-rates300
POST /api/v1/invoice/refund30
Invoice amount

From $0.01 to $100 000 per invoice. Need a higher ceiling? Ask us.

FastPay © 2026 FastPay Terms of use Privacy policy AML