Payflux Sign in
API v1 · Live

Developer Documentation

Accept crypto payments, track custody and pay out — one REST API with idempotency, signed webhooks and a double-entry ledger underneath.

Base URL https://payflux.space/api/v1
Getting started Authentication Idempotency Create payment Payment status List payments Payment links Custody Withdrawals Webhooks Errors Rate limits

Getting started

Four steps from zero to your first crypto payment. Everything is live — there is no test mode.

1

Create an account

Sign up with email, verify, and confirm your account. Free — you only pay per successful payment.

2

Create a project

Projects isolate keys, links, webhooks and payments per product or site.

3

Generate a live API key

Dashboard → API keys → New. The secret key (sk_live_…) is shown exactly once — store it safely.

4

Make your first call

Create a payment with one POST below. Redirect the customer to the returned payment_url, or use a shared payment link.

Authentication

All requests authenticate with your secret key as a Bearer token. Secret keys are powerful — use them server-side only, never in frontend JavaScript.

HEADERAuthorization: Bearer sk_live_…
pk_live_… publicIdentifies your project in hosted-checkout URLs and client-side contexts. Safe to expose.
sk_live_… secretFull API access. Stored as a SHA-256 hash — visible only at creation. Rotate by revoking + creating.

Requests with a revoked key return 401 INVALID_API_KEY. Keys are bound to one project — payments created with a key are scoped to that project.

Idempotency

Networks retry. Idempotency keys make retries safe.

Send any unique string per logical operation:

Idempotency-Key: order-10025

On write endpoints (POST /payments, POST /withdrawals) replaying the same key returns the original response with an Idempotent-Replay: true header — a second charge is never created. Keys are scoped to your API key and endpoint, max 80 characters.

Create a payment

POST/v1/payments

Creates a crypto payment and returns a deposit address plus a hosted payment URL. The customer pays; we settle to your custody and notify your webhook.

Body parameters

amount number · requiredAmount in fiat (e.g. USD). Positive, up to 2 decimals.
currency stringFiat pricing currency. Default USD.
pay_currency string · requiredCrypto the customer pays with — provider code like usdttrc20, btc, eth. Must be enabled on the platform.
order_id stringYour internal order reference (max 120). Returned in webhooks for matching.
description stringShown on the hosted checkout (max 500).
success_url stringWhere the customer is redirected after paying.
cancel_url stringRedirect if the customer cancels.
curl -X POST https://payflux.space/api/v1/payments \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-10025" \
  -d '{
    "amount": 100,
    "currency": "USD",
    "pay_currency": "usdttrc20",
    "order_id": "ORDER-10025",
    "description": "Premium Package",
    "success_url": "https://yoursite.com/thanks",
    "cancel_url": "https://yoursite.com/cancel"
  }'
<?php
$ch = curl_init('https://payflux.space/api/v1/payments');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'Authorization: Bearer sk_live_••••',
    'Content-Type: application/json',
    'Idempotency-Key: order-10025',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'amount'       => 100,
    'currency'     => 'USD',
    'pay_currency' => 'usdttrc20',
    'order_id'     => 'ORDER-10025',
    'description'  => 'Premium Package',
  ]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data['payment_id'];   // pay_…
echo $data['payment_url'];  // hosted checkout for the customer
const res = await fetch('https://payflux.space/api/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_••••',
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-10025',
  },
  body: JSON.stringify({
    amount: 100,
    currency: 'USD',
    pay_currency: 'usdttrc20',
    order_id: 'ORDER-10025',
    description: 'Premium Package',
  }),
});
const data = await res.json();
console.log(data.payment_id, data.payment_url);
import requests

res = requests.post(
    'https://payflux.space/api/v1/payments',
    headers={
        'Authorization': 'Bearer sk_live_••••',
        'Idempotency-Key': 'order-10025',
    },
    json={
        'amount': 100,
        'currency': 'USD',
        'pay_currency': 'usdttrc20',
        'order_id': 'ORDER-10025',
        'description': 'Premium Package',
    },
)
data = res.json()
print(data['payment_id'], data['payment_url'])

Response · 201

{
  "success": true,
  "payment_id": "pay_928271ab47c1f2d3e4",
  "status": "waiting_payment",
  "amount": 100,
  "currency": "USD",
  "pay_currency": "USDTTRC20",
  "pay_address": "TXk9rR…",
  "pay_amount": 99.75,
  "network": "tron",
  "extra_id": null,
  "payment_url": "https://payflux.space/pay/pay_928271ab47c1f2d3e4",
  "created_at": "2026-09-19T10:56:29+00:00"
}

Retrieve a payment

GET/v1/payments/{id}

Poll this endpoint during checkout — but prefer webhooks. Statuses are normalized:

StatusMeaning
createdCreated, awaiting provider address
waiting_paymentAddress issued — waiting for the customer's transfer
confirmingTransaction seen, awaiting blockchain confirmations
payment_detectedConfirmed — settling to custody
completedSettled and credited to your custody
partially_paidA lower amount arrived (credited as paid)
failed / expired / refundedTerminal — nothing credited
curl https://payflux.space/api/v1/payments/pay_928271ab \
  -H "Authorization: Bearer sk_live_••••"
<?php
$ch = curl_init('https://payflux.space/api/v1/payments/pay_928271ab');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => ['Authorization: Bearer sk_live_••••'],
]);
$data = json_decode(curl_exec($ch), true);
echo $data['payment']['status'];
const data = await fetch('https://payflux.space/api/v1/payments/pay_928271ab', {
  headers: { 'Authorization': 'Bearer sk_live_••••' },
}).then(r => r.json());
console.log(data.payment.status);
import requests
data = requests.get(
    'https://payflux.space/api/v1/payments/pay_928271ab',
    headers={'Authorization': 'Bearer sk_live_••••'},
).json()
print(data['payment']['status'])

List payments

GET/v1/payments?status=completed&limit=25

Newest first. Filters: status (any normalized status), limit (1–100, default 25).

Returns { "success": true, "count": n, "payments": [ …same objects as Retrieve… ] }

Custody balances

GET/v1/custody/balances

Every asset held for you, reconciled against the ledger. available is withdrawable; pending is clearing; reserved is held for in-flight payouts.

curl https://payflux.space/api/v1/custody/balances \
  -H "Authorization: Bearer sk_live_••••"
<?php
// returns every custody asset with available / pending / reserved
$data = json_decode(file_get_contents('https://payflux.space/api/v1/custody/balances', false,
  stream_context_create(['http' => ['header' => 'Authorization: Bearer sk_live_••••']])), true);
print_r($data['balances']);
const data = await fetch('https://payflux.space/api/v1/custody/balances', {
  headers: { 'Authorization': 'Bearer sk_live_••••' },
}).then(r => r.json());
data.balances.forEach(b => console.log(b.currency, b.available));
import requests
data = requests.get(
    'https://payflux.space/api/v1/custody/balances',
    headers={'Authorization': 'Bearer sk_live_••••'},
).json()
for b in data['balances']:
    print(b['currency'], b['available'])

Response

{
  "success": true,
  "balances": [
    { "currency": "USDTTRC20", "available": 2450.00,
      "pending": 125.00, "reserved": 0.00, "total": 2575.00 },
    { "currency": "BTC", "available": 0.0421,
      "pending": 0, "reserved": 0, "total": 0.0421 }
  ]
}

Withdrawals

POST/v1/withdrawals

Pays available custody funds to a pre-verified whitelisted wallet (manage wallets in the dashboard). Funds are reserved atomically — the platform never allows a negative balance. Large or flagged amounts enter review automatically.

currency string · requiredProvider code, e.g. usdttrc20. Must be withdrawal-enabled.
amount number · requiredGross amount. Fee is applied server-side per platform rules; net_amount is what lands in the wallet.
wallet_id number · requiredID of a verified wallet (dashboard → Payout → Wallets).
curl -X POST https://payflux.space/api/v1/withdrawals \
  -H "Authorization: Bearer sk_live_••••" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: payout-001" \
  -d '{
    "currency": "usdttrc20",
    "amount": 50,
    "wallet_id": 1
  }'
<?php
// wallet_id = a verified wallet from your Payflux dashboard
$ch = curl_init('https://payflux.space/api/v1/withdrawals');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'Authorization: Bearer sk_live_••••',
    'Content-Type: application/json',
    'Idempotency-Key: payout-001',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'currency' => 'usdttrc20',
    'amount'   => 50,
    'wallet_id'=> 1,
  ]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data['withdrawal_id'];
const data = await fetch('https://payflux.space/api/v1/withdrawals', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_••••',
    'Content-Type': 'application/json',
    'Idempotency-Key': 'payout-001',
  },
  body: JSON.stringify({
    currency: 'usdttrc20',
    amount: 50,
    wallet_id: 1,
  }),
}).then(r => r.json());
console.log(data.withdrawal_id, data.status);
import requests
data = requests.post(
    'https://payflux.space/api/v1/withdrawals',
    headers={
        'Authorization': 'Bearer sk_live_••••',
        'Idempotency-Key': 'payout-001',
    },
    json={
        'currency': 'usdttrc20',
        'amount': 50,
        'wallet_id': 1,
    },
).json()
print(data['withdrawal_id'])

Check a withdrawal

GET/v1/withdrawals/{id}
Returns status (created → processing → completed / failed), fee breakdown and, once settled, the on-chain tx_hash. A failed payout automatically returns funds to your available balance.

Webhooks

Server-to-server JSON notifications for every event. Configure endpoints in Dashboard → Webhooks; deliveries retry with exponential backoff (up to 8 attempts) and auto-disable after 25 consecutive failures.

Events

payment.created payment.detected payment.confirming payment.completed payment.partially_paid payment.failed payment.expired payment.refunded withdrawal.created withdrawal.processing withdrawal.completed withdrawal.failed

Example payload · payment.completed

POST /webhooks/payflux
Payflux-Signature: 5f8a…   // HMAC-SHA256 hex

{
  "event_id": "evt_9b1c…",
  "event_type": "payment.completed",
  "created_at": "2026-09-19T10:56:29+00:00",
  "data": {
    "payment_id": "pay_928271ab47c1f2d3e4",
    "order_id": "ORDER-10025",
    "amount": 100,
    "currency": "USD",
    "actually_paid": 99.75,
    "outcome_currency": "usdttrc20",
    "status": "completed"
  }
}

Verify every delivery. Compute HMAC-SHA256 over the raw body with your whsec_… signing secret and compare to the Payflux-Signature header using a constant-time check. Reject anything else with 401.

<?php
// POST https://yoursite.com/webhooks/payflux
$raw   = file_get_contents('php://input');
$sig   = $_SERVER['HTTP_PAYFLUX_SIGNATURE'] ?? '';
$secret = 'whsec_…'; // your endpoint signing secret

$expected = hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($raw, true);
if ($event['event_type'] === 'payment.completed') {
    // fulfill the order — $event['data']['payment_id'] etc.
}
http_response_code(200);
// Express example
app.post('/webhooks/payflux', (req, res) => {
  const raw = req.rawBody;               // needs express.raw() middleware
  const sig = req.get('Payflux-Signature');
  const expected = crypto
    .createHmac('sha256', 'whsec_…')
    .update(raw)
    .digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(raw);
  if (event.event_type === 'payment.completed') {
    // fulfill the order
  }
  res.sendStatus(200);
});

Deliveries may repeat — treat handlers as idempotent and key off event_id. Answer 2xx quickly; anything else schedules a retry. Failed events can be replayed from the dashboard.

Errors

Consistent format across the API:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient available balance."
  }
}
HTTPCodeWhen
400INVALID_REQUESTMalformed or missing parameters
400INVALID_CURRENCYpay_currency not available / not enabled
400INVALID_WALLETMissing, unverified or mismatched wallet
400INSUFFICIENT_BALANCEWithdrawal exceeds available custody
401INVALID_API_KEYMissing, malformed, revoked key
403FORBIDDENAccount restricted / API disabled
404PAYMENT_NOT_FOUNDUnknown payment for this account
429RATE_LIMIT_EXCEEDEDToo many requests — see Retry-After
502PROVIDER_ERRORUpstream payment provider hiccup — retry safely

Rate limits

Default 100 requests / minute per API key (per-key overrides configurable). Exceeding it returns 429 RATE_LIMIT_EXCEEDED with a Retry-After: 60 header.

Every call — including rate-limited ones — is logged with method, endpoint, IP and duration for auditability.

Ready to build?

Free account, live keys in under a minute — pay only per successful payment.

Create free account