Getting started
Four steps from zero to your first crypto payment. Everything is live — there is no test mode.
Create an account
Sign up with email, verify, and confirm your account. Free — you only pay per successful payment.
Create a project
Projects isolate keys, links, webhooks and payments per product or site.
Generate a live API key
Dashboard → API keys → New. The secret key (sk_live_…) is shown exactly once — store it safely.
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.
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
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
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 customerconst 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
Poll this endpoint during checkout — but prefer webhooks. Statuses are normalized:
| Status | Meaning |
|---|---|
| created | Created, awaiting provider address |
| waiting_payment | Address issued — waiting for the customer's transfer |
| confirming | Transaction seen, awaiting blockchain confirmations |
| payment_detected | Confirmed — settling to custody |
| completed | Settled and credited to your custody |
| partially_paid | A lower amount arrived (credited as paid) |
| failed / expired / refunded | Terminal — 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
Newest first. Filters: status (any normalized status), limit (1–100, default 25).
Payment links
No-code checkout you can share anywhere. Create and manage links in the dashboard; list them via API:
Visiting a link opens the hosted checkout — currency/network picker, QR, countdown. For blank-amount links the customer types the amount there.
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
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.
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
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
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."
}
}| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | Malformed or missing parameters |
| 400 | INVALID_CURRENCY | pay_currency not available / not enabled |
| 400 | INVALID_WALLET | Missing, unverified or mismatched wallet |
| 400 | INSUFFICIENT_BALANCE | Withdrawal exceeds available custody |
| 401 | INVALID_API_KEY | Missing, malformed, revoked key |
| 403 | FORBIDDEN | Account restricted / API disabled |
| 404 | PAYMENT_NOT_FOUND | Unknown payment for this account |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests — see Retry-After |
| 502 | PROVIDER_ERROR | Upstream 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