API Overview

Uapi provides a high-performance, non-custodial crypto payment infrastructure for global merchants. Our API makes complex blockchain interactions as simple as traditional payments.

Base URL

https://uapi.io/api/v1

1 Authentication

All requests must include your API Key in HTTP headers. Keys are split into Test Key (sandbox) and Live Key (production).

HTTP Header
X-API-KEY: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Every request goes through these 6 checks (in order)

# Check On failure How to fix
1X-API-KEY header present401 Missing API KeyAdd the X-API-KEY header
2Caller IP not on system blocklist403 IP BlockedAuto-blocked after repeated failed attempts; contact support
3API Key valid and account active403 Invalid API Key / Account suspendedCopy the current key from API Settings; suspended accounts need support
4Plan not expired (paid plans)403 Plan expiredRenew or downgrade to Free
5Caller IP in your API IP whitelist (if enabled)403 IP not in whitelistAdd the caller IP under API Security; or disable the whitelist
6Request origin domain bound to this account403 Access Denied: ...Two options: browser-side requests must send Origin/Referer; server-to-server calls must include domain (a bound website) in the JSON body

Extra "chain" permission check (only on /order/create.php)

When you pass the chain field, the API also checks whether that chain is enabled on your current plan. Free typically allows TRC20 only — upgrading to Pro unlocks more chains. Failure returns 403 Access Denied: Chain '…' is not enabled for your current plan. See Subscription / Plan in the console for your currently enabled chains.

Domain binding quick reference

Browser-side: the browser sends Origin or Referer; UAPI matches that to a bound domain.
Server-side: curl / backend SDK calls do not send those headers, so include "domain": "yoursite.com" in the JSON body.
Bind sites under Merchant Console → API Settings → Website Bindings.

Integration Flow

1

Create Order

Merchant calls the API with amount and network, then receives a payment link (payment_url).

2

User Pays

Redirect users to checkout, where they complete payment via QR scan or transfer.

3

Confirm & Callback

After on-chain confirmation, the system sends a webhook and updates order status automatically.

No-Code Integration

If you do not want to write code, you can use our Payment Link feature:

  • Generate a dedicated payment page in one click from the merchant console.
  • Set fixed amounts or allow customers to enter custom amounts.
  • Share the link directly through social media, Telegram, or WhatsApp.
  • After successful payment, settlement details remain available in your dashboard.

Create Order

POST

Create a brand-new payment order and receive a checkout link.

Endpoint

/order/create.php
Parameter Type Required Description
amount Float YES Order amount (USDT)
chain String YES Network: trc20, erc20, bsc, solana, polygon, etc.
merchant_order_id String YES Your merchant order number (unique)
notify_url String NO Webhook URL. A POST callback will be sent after successful payment.
domain String YES* Required for server-to-server requests (e.g., curl/backend SDK). Use your bound website domain, e.g. yoursite.com. It can be omitted if Origin/Referer is present.
currency String NO Settlement token: USDT (default) or USDC. Note: the backend accepts currency only, not token.
{
  "status": "success",
  "data": {
    "order_no": "PAY2024...",          // UAPI-generated order number (used by every subsequent call)    "amount": 100.001234,              // Order amount (echoed back)    "currency": "USDT",                // Settlement token    "chain": "trc20",                  // Settlement chain    "expire_in": 600,                  // Order TTL in seconds (relative)    "payment_url": "https://...",      // Checkout URL — redirect the buyer here    "fast_sync_enabled": true          // Whether fast on-chain confirmation is enabled for this order  }
}

⚠️ Older docs referenced order_id / token / payment_address / qr_code / expire_at (timestamp). Those fields are NOT returned — integrate against the keys above.

Check Order Status

GET
This endpoint does not use X-API-KEY. It authenticates with the per-order pay_access_token returned by create (already embedded in payment_url) — designed mainly for the checkout page to poll. Server integrators typically receive payment success via the Webhook callback rather than polling.

Endpoint

GET /api/v1/order/status.php?order_no=PAY...&token=ACCESS_TOKEN

Query Parameters

Param Required Meaning
order_noYESUAPI order number from create response
tokenYESPer-order access token; extract it from the payment_url query string returned at create time

Responses (by state)

// Paid{ "status": "paid", "tx_hash": "0x123abc..." }

// Awaiting payment{ "status": "pending" }

// Order expired{ "status": "expired" }

// Error{ "status": "error", "error": "Invalid token" }

Rate limits: 30 / min per IP, 40 / 10s per order across all IPs. Server integrators should rely on Webhook callbacks instead of polling.

Submit Dispute

POST

When a buyer underpaid or paid in the wrong currency, the merchant can flag the order as disputed and optionally rewrite the recorded amount / currency to match what was actually received.

Endpoint

POST /api/v1/order/dispute.php

Headers

X-API-KEY: sk_live_xxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json

Body

Field Required Meaning
merchant_order_idYES*Your merchant order ID. Either this or order_no must be provided.
order_noYES*UAPI order number
domainYES*Required for server-side calls; must be a bound domain (same rule as create.php)
modeNOoriginal (default, keep original amount) or adjusted (rewrite using new_amount / new_currency)
new_amountNOUsed when mode=adjusted; cannot exceed the original amount (clamped if it does)
new_currencyNOUsed when mode=adjusted; USDT or USDC
noteNOFree-form note appended to the order's notes column

Success Response

{
  "status": "success",
  "data": {
    "order_id": 12345,
    "order_no": "UAPI20260408001",
    "merchant_order_id": "ORD-001",
    "order_status": "disputed",
    "amount": "100.000000",
    "currency": "USDT",
    "mode": "original"
  }
}

Rate limit: 20 / min per IP. After submission the order status flips to disputed — no further Webhook will fire.

Webhook Callback

Once the on-chain transaction is confirmed, UAPI immediately sends a POST callback to the notify_url you passed when creating the order.

If notify_url is omitted when creating an order, the default webhook URL configured in Merchant Console > API Settings is used instead.

Request Headers

Header Meaning
Content-TypeAlways application/json
X-UAPI-SignatureHMAC-SHA256 hex digest (no sha256= prefix)
X-UAPI-TimestampUnix timestamp (seconds) used as part of the signed payload
X-UAPI-EventCurrently always order.paid
X-UAPI-Event-IDPer-event unique ID (16 hex chars); use it to dedupe retries

Request Body (flat)

{
  "status": "paid",
  "order_no": "UAPI20260408001",
  "merchant_order_id": "ORD-001",
  "amount": "100.00",
  "chain": "trc20",
  "currency": "USDT",
  "tx_hash": "0xabcdef1234567890...",
  "paid_at": "2026-04-08T10:05:00+00:00"
}

⚠️ Field names: order_no (not order_id), currency (not token), tx_hash (not txid). The body is flat — there is no wrapping event/data object.

Signature Input & Verification

The signed payload is four fields concatenated: order_no + amount + merchant_order_id + timestamp. The secret is your API Key — there is no separate webhook secret.

<?php
$rawBody   = file_get_contents('php://input');
$payload   = json_decode($rawBody, true);
$signature = $_SERVER['HTTP_X_UAPI_SIGNATURE']  ?? '';
$timestamp = $_SERVER['HTTP_X_UAPI_TIMESTAMP']  ?? '';
$apiKey    = 'sk_live_your_api_key';

// reject signatures older than 5 minutes (anti-replay)
if (abs(time() - (int)$timestamp) > 300) {
    http_response_code(401); exit('Stale timestamp');
}

$signInput = $payload['order_no']
           . $payload['amount']
           . $payload['merchant_order_id']
           . $timestamp;
$expected = hash_hmac('sha256', $signInput, $apiKey);

if (!hash_equals($expected, $signature)) {
    http_response_code(401); exit('Invalid signature');
}

// dedupe on X-UAPI-Event-ID, then process the order
// return HTTP 200 to acknowledge
http_response_code(200); echo 'success';

Response requirement: return HTTP 2xx (or a body containing success). Otherwise UAPI retries up to 3 times (10s timeout each). The X-UAPI-Event-ID header stays constant across retries — use it to dedupe.

Order State Machine

Status Description
pending Created and waiting for user payment with the exact amount.
paid On-chain amount and address matched; tx hash recorded and callback sent.
expired Not paid within validity period (usually 10-20 minutes); automatically marked as expired.

Rate Limits, Idempotency & Amount Precision

The Four Rate-Limit Layers (in order)

Layer Scope Quota On exceed
1. Per-IP 黑名单Site-wide, by caller IPAuto-block on repeated failures403 IP Blocked: ...
2. Per-endpoint burstPer endpoint × IP × 60s windoworder/create.php: 30 / min
order/status.php: 30 / min
order/dispute.php: 20 / min
store/create_order.php: 20 / min
429 Too Many Requests
3. Per-order burstSingle order across all IPs × 10s40 / 10s429 Order polling too frequent
4. Daily quotaAccount-level, by PlanFree 100 / day
Pro 20,000 / day
Business 50,000 / day
429 Daily quota exceeded

Idempotency Guarantees

  • Create Order: POSTing the same merchant_order_id returns the existing order (same order_no and payment_url) — no duplicate is created.
  • Webhook: Each event has a unique X-UAPI-Event-ID; retries reuse the same id — dedupe on it.
  • Concurrency: Lock by order_no (e.g. Redis) when handling webhooks to serialize per-order work.

Amount Precision & Anti-collision

Request amount is a plain decimal in the range (0, 1,000,000].

Response amount may be your input padded with up to 6 random decimal digits (e.g. 100.00 → 100.001234). This is intentional: the trailing micro-amount lets the on-chain watcher distinguish two simultaneous orders with the same headline price.

Customers must transfer the response amount exactly (a single missing micro-unit causes the watcher to skip the order).

Do NOT truncate the response amount to 2 decimals client-side; show the exact UAPI-returned amount in the QR / instructions.

Supported Networks & Precision

Network (Slug) Precision Token Notes
trc20 6 USDT Tron network with fast confirmation and very low fees.
bsc 18 USDT/BNB Binance Smart Chain.
erc20 18 USDT/ETH Ethereum mainnet (higher gas fees).
polygon 6/18 USDT/MATIC Layer 2 scaling network.
solana 6/9 USDT/USDC High-performance non-EVM chain.
arbitrum 18 USDT Ethereum L2 rollup network.

Errors & Error Codes

External API endpoints (X-API-KEY) currently emit errors in two shapes — a short single-field form and a structured status/error/code form. Your client should handle both.

Short form (most auth / rate-limit errors)

{ "error": "Invalid API Key" }

Structured form (business errors)

{
  "status": "error",
  "error": "Plan expired. Please upgrade/renew.",
  "code": "PLAN_EXPIRED"
}

HTTP Status Codes

Status Meaning Common Trigger
200SuccessOrder created / data returned
400Bad requestMissing field / unsupported chain / USDC on TRC20
401UnauthorizedMissing X-API-KEY header
403ForbiddenInvalid API key / suspended account / plan expired / IP blocked / IP not in whitelist / chain not enabled on plan / origin not bound
429Rate limitedDaily quota exhausted / per-IP burst / per-order query burst
500Server errorWallet not provisioned / RPC failure / DB exception

Common error messages & how to fix

error How to fix
Missing API KeyAdd X-API-KEY: sk_live_xxx header
Invalid API KeyKey wrong or rotated. Console > API Settings > copy current key
IP not in whitelistAdd the caller IP to whitelist in API Security settings
Plan expiredRenew or upgrade your plan
Access Denied: Chain '...' is not enabled for your current planChain requires a higher plan tier; switch chain or upgrade
Access Denied: domain not boundBind the request origin in Settings > Websites, or pass `domain` in the JSON body for S2S calls
Currency '...' is not supported on chain '...'USDC is not available on TRC20; pick another chain or USDT
Too many requestsBack off and retry; or upgrade plan for higher quota

Tools & Versions

TLS Requirement

For security, our API requires TLS 1.2 or higher. Older SSL/TLS versions cannot establish a connection.

Data Encoding

All request and response bodies must use UTF-8. Ensure your Content-Type is set to application/json.

Developer FAQ

Can order expiration be customized?

Default validity is 10-20 minutes. For longer expiration, contact support or configure it in Merchant Console > API Settings. Longer windows may increase amount-collision probability.

Why does the actual payable amount include random decimals?

This enables precise order matching in non-custodial wallets. Since multiple transfers may share the same integer amount on-chain, adding a tiny random decimal (e.g., 100.001234) keeps each order unique under your wallet address.

Multi-language Integration Examples

PHP
$apiKey = 'sk_live_xxx';
$data = [
    'amount' => 100.0,
    'chain' => 'trc20',
    'merchant_order_id' => 'MY_ORDER_001',
    'notify_url' => 'https://yoursite.com/callback',
    'domain' => 'yoursite.com'
];

$ch = curl_init('https://uapi.io/api/v1/order/create.php');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'X-API-KEY: ' . $apiKey,
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$result = json_decode($response, true);
echo $result['data']['payment_url'] ?? 'Create order failed';
Node.js (Axios)
const axios = require('axios');

const createOrder = async () => {
  const { data } = await axios.post('https://uapi.io/api/v1/order/create.php', {
    amount: 100.0,
    chain: 'trc20',
    merchant_order_id: 'MY_ORDER_001',
    notify_url: 'https://yoursite.com/callback',
    domain: 'yoursite.com'
  }, {
    headers: {
      'X-API-KEY': 'sk_live_xxx',
      'Content-Type': 'application/json'
    }
  });
  console.log(data.data.payment_url);
};