Introduction

Getting Started

ZyndPay API Reference

API v1 · USDT TRC20 on TRON

The ZyndPay API is a RESTful payment gateway for accepting USDT on the TRON blockchain. Resource-oriented URLs, JSON bodies, and a consistent response envelope make it easy to integrate in any environment.

All amounts are in USDT (TRC20). The minimum payin is 20 USDT. A $2 USDT minimum fee floor and flat $2 USDT payout fee apply to all transactions.

Base URL

https://api.zyndpay.com/v1

All responses are wrapped in a consistent envelope:

JSON — response envelope
{
  "success": true,
  "data": { ... },
  "message": "optional string",
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 100,
    "totalPages": 5
  }
}

Getting Started

Authentication

Include your API key in the X-Api-Key header on every request. Alternatively, pass it as a Bearer token in the Authorization header.

cURL
curl https://api.zyndpay.com/v1/payin \
  -H "X-Api-Key: zyp_live_sk_..."

API key prefixes let you identify the key type at a glance:

ParameterTypeRequiredDescription
zyp_live_sk_...secretrequiredLive secret key — full API access. Never expose client-side.
zyp_live_pk_...publishableoptionalLive publishable key — limited read access, safe for browsers.
zyp_test_sk_...secretoptionalSandbox secret key — identical to live but no real transactions.
zyp_test_pk_...publishableoptionalSandbox publishable key.
zyp_rk_...restrictedoptionalRestricted key — scoped to specific operations only.
Warning:Never embed zyp_live_sk_ or zyp_test_sk_ in client-side code, mobile apps, or public repositories.

Getting Started

Quick Start

Create your first payment in three steps — no SDK required, just a single HTTP request.

1

Get your API key

Sign up at dashboard.zyndpay.com, complete KYB verification, and copy your live or sandbox secret key.

2

Create a payin

POST /v1/payin with your amount and an externalRef (your order ID). You receive a TRON deposit address.

3

Receive the webhook

When your customer sends USDT to the address, ZyndPay fires a payin.confirmed webhook and credits your balance.

Payins

Create a Payin

Generates a unique TRON wallet address for your customer to send USDT to. The address expires after expiresInSeconds seconds (default 1 hour).

POST/v1/payin

Request body

ParameterTypeRequiredDescription
amountstringrequiredAmount in USDT as a decimal string (e.g. "100.00"). Minimum: "20.00".
externalRefstringoptionalYour order or reference ID (optional). Must be unique per merchant if provided.
expiresInSecondsintegeroptionalSeconds until the address expires. Minimum: 900. Default: 3600.
successUrlstringoptionalURL to redirect the customer after a successful payment.
cancelUrlstringoptionalURL to redirect the customer if the payment expires or is cancelled.
metadataobjectoptionalArbitrary key-value pairs stored with the payment and included in webhooks.
cURL
curl -X POST https://api.zyndpay.com/v1/payin \
  -H "X-Api-Key: zyp_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "100.00",
    "externalRef": "order_123",
    "expiresInSeconds": 3600,
    "successUrl": "https://yoursite.com/payment/success",
    "cancelUrl": "https://yoursite.com/payment/cancel"
  }'
JSON — response
{
  "success": true,
  "data": {
    "id": "cma1xyz8f0001yx5k9abc1234",
    "depositAddress": "TRXabc123def456ghi789jkl",
    "amount": "100.00",
    "status": "PENDING",
    "externalRef": "order_123",
    "expiresAt": "2026-03-06T12:00:00.000Z",
    "createdAt": "2026-03-06T11:00:00.000Z"
  }
}

Get a Payin

Retrieve a single payin by its ID.

GET/v1/payin/:id
JSON — response
{
  "success": true,
  "data": {
    "id": "cma1xyz8f0001yx5k9abc1234",
    "depositAddress": "TRXabc123def456ghi789jkl",
    "amount": "100.00",
    "amountReceived": "100.00",
    "fee": "3.00",
    "netAmount": "97.00",
    "status": "CONFIRMED",
    "txHash": "abc123def456...",
    "externalRef": "order_123",
    "confirmedAt": "2026-03-06T11:01:02.000Z",
    "createdAt": "2026-03-06T11:00:00.000Z"
  }
}

Payment statuses

PENDINGAwaiting payment
CONFIRMINGFunds received, waiting for 20 confirmations
CONFIRMED20+ confirmations — balance credited
UNDERPAIDLess than expected received
OVERPAIDMore than expected received
EXPIREDAddress expired without payment

List Payins

Returns a paginated list of payins, newest first.

GET/v1/payin
ParameterTypeRequiredDescription
pageintegeroptionalPage number. Default: 1.
limitintegeroptionalResults per page. Default: 20. Max: 100.
statusstringoptionalFilter by status: PENDING, CONFIRMING, CONFIRMED, EXPIRED, UNDERPAID, or OVERPAID.
currencystringoptionalFilter by currency (e.g. USDT_TRC20).
JSON — response
{
  "success": true,
  "data": [ ... ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 134,
    "totalPages": 7
  }
}

Payouts

Create a Payout

Payouts let you send USDT from your ZyndPay balance to any TRON address — pay vendors, refund customers, or distribute affiliate commissions. A flat fee of $2 USDT is deducted from your balance on top of the payout amount.

Note:Payouts vs Withdrawals: Payouts send funds to any third-party address (vendors, customers, affiliates). Withdrawals move funds to your own pre-saved wallet addresses.
POST/v1/payout
Tip:Payouts are idempotent. Pass a unique Idempotency-Key header (UUID recommended) to safely retry failed requests without creating duplicates.

Request body

ParameterTypeRequiredDescription
amountstringrequiredAmount in USDT to send (e.g. "50.00"). Minimum: "20.00". The $2 USDT processing fee is charged separately.
destinationAddressstringrequiredTRON (TRC20) wallet address of the recipient. Must match the format T followed by 33 base58 characters.
currencystringoptionalCurrency to send. Default: USDT_TRC20.
chainstringoptionalBlockchain network. Default: TRON.
externalRefstringoptionalYour internal reference ID for this payout (e.g. vendor invoice number).
metadataobjectoptionalArbitrary key-value pairs stored with the payout.
cURL
curl -X POST https://api.zyndpay.com/v1/payout \
  -H "X-Api-Key: zyp_live_sk_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "amount": "50.00",
    "destinationAddress": "TXYZabc123def456ghi789jkl012mno345",
    "externalRef": "payout_vendor_456"
  }'
JSON — response
{
  "success": true,
  "data": {
    "transactionId": "cma2abc9g0002yz6l0def5678",
    "status": "PROCESSING",
    "processingFee": "2.00",
    "requiresManualApproval": false,
    "currentPayinFee": "3.0%",
    "currentTier": "STARTER"
  }
}

Get a Payout

Retrieve a single payout by its ID, including its current status and transaction details.

GET/v1/payout/:id

Payout statuses

PENDINGAwaiting manual approval (amount > $50K)
PROCESSINGApproved, preparing broadcast
BROADCASTSubmitted to TRON network
CONFIRMEDConfirmed on-chain
FAILEDBroadcast failed — balance refunded
CANCELLEDCancelled before broadcast

List Payouts

Returns a paginated list of payouts, newest first.

GET/v1/payout
ParameterTypeRequiredDescription
pageintegeroptionalPage number. Default: 1.
limitintegeroptionalResults per page. Default: 20. Max: 100.
JSON — response
{
  "success": true,
  "data": {
    "items": [ ... ],
    "total": 42
  }
}

Withdrawals

Request a Withdrawal

Withdrawals let merchants move their settled USDT balance out of ZyndPay to one of their pre-saved wallet addresses. A flat fee of $2 USDT is deducted from the amount.

Note:Destination addresses must be saved in your dashboard before you can withdraw to them. If addressId is omitted, ZyndPay uses your account's primary withdrawal address.
POST/v1/withdrawals
Tip:Withdrawals are idempotent. Pass a unique Idempotency-Key header (UUID recommended) to safely retry failed requests without creating duplicates.
ParameterTypeRequiredDescription
amountstringrequiredAmount in USDT to withdraw (e.g. "500.00"). The $2 USDT fee is deducted from this amount.
addressIdUUIDoptionalID of the saved withdrawal address to use. Defaults to your primary address if omitted.
cURL
curl -X POST https://api.zyndpay.com/v1/withdrawals \
  -H "X-Api-Key: zyp_live_sk_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "amount": "500.00",
    "addressId": "addr_cma1xyz8f0001yx5k"
  }'
JSON — response
{
  "success": true,
  "data": {
    "id": "wdr_cma1xyz8f0001yx5k",
    "amount": "500.00",
    "fee": "2.00",
    "netAmount": "498.00",
    "destinationAddress": "TRXabc123def456ghi789jkl",
    "status": "PENDING",
    "createdAt": "2026-03-06T11:00:00.000Z"
  }
}

Get a Withdrawal

Retrieve a single withdrawal request by its ID.

GET/v1/withdrawals/:id
PENDING_REVIEWAwaiting admin review
APPROVEDApproved, queued for processing
BROADCASTSubmitted to TRON network
CONFIRMEDConfirmed on-chain
REJECTEDRejected by admin review
FAILEDBroadcast failed — balance refunded
CANCELLEDCancelled before broadcast

Cancel a Withdrawal

Cancels a withdrawal request that is still in PENDING_REVIEW status (before it is approved and broadcast to the TRON network). The amount is immediately returned to your balance.

DELETE/v1/withdrawals/:id

Webhooks

Overview

ZyndPay sends HTTP POST requests to your registered endpoint URLs when payment events occur. Register endpoints from the Webhooks page in your merchant dashboard.

Tip:Your endpoint must return a 2xx status within 10 seconds. Failed deliveries are retried with exponential backoff up to 5 times over ~2 hours.
JSON — example payload
{
  "id": "wh_cma1xyz8f0001yx5k",
  "type": "payin.confirmed",
  "data": {
    "id": "cma1xyz8f0001yx5k9abc1234",
    "amount": "100.00",
    "fee": "3.00",
    "netAmount": "97.00",
    "status": "CONFIRMED",
    "externalRef": "order_123",
    "txHash": "abc123def456...",
    "confirmedAt": "2026-03-06T11:01:02.000Z"
  },
  "createdAt": "2026-03-06T11:01:03.000Z"
}

Signature Verification

Every webhook delivery includes an X-ZyndPay-Signature header. Always verify it before processing the event.

X-ZyndPay-Signature: t=1741258862,v1=abc123def456...

The signature is HMAC-SHA256(timestamp + "." + raw_body, webhook_secret). Always use the raw request body — parsing JSON first will cause verification failures.

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

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-zyndpay-signature'];
  const [tPart, v1Part] = sig.split(',');
  const timestamp = tPart.split('=')[1];
  const received  = v1Part.split('=')[1];

  // Reject events older than 5 minutes
  const age = Math.abs(Date.now() / 1000 - parseInt(timestamp));
  if (age > 300) return res.status(400).send('Webhook too old');

  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${req.body}`)
    .digest('hex');

  // Use timing-safe comparison to prevent timing attacks
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
    return res.status(400).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Handle event...
  res.json({ received: true });
});
Python
import hmac, hashlib, time

def verify_webhook(payload: bytes, sig_header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in sig_header.split(","))
    timestamp = parts["t"]
    received  = parts["v1"]

    # Reject events older than 5 minutes
    if abs(time.time() - int(timestamp)) > 300:
        return False

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{payload.decode()}".encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, received)

Events

ParameterTypeRequiredDescription
payin.createdeventoptionalFired when a new payin is created and a deposit address is generated.
payin.confirmingeventoptionalFired when funds are received and awaiting 20 block confirmations.
payin.confirmedeventoptionalFired when a payin reaches 20 on-chain confirmations. Merchant balance is credited.
payin.expiredeventoptionalFired when a payin address expires without receiving the expected amount.
payin.underpaideventoptionalFired when a payin receives less than the requested amount.
payin.overpaideventoptionalFired when a payin receives more than the requested amount.
withdrawal.requestedeventoptionalFired when a new withdrawal is requested and queued for review.
withdrawal.confirmedeventoptionalFired when a withdrawal is confirmed on-chain.
withdrawal.failedeventoptionalFired when a withdrawal broadcast fails. Amount is refunded to balance.

SDKs & Plugins

Node.js SDK

The official TypeScript SDK with full type safety and built-in webhook verification.

npm
npm install @zyndpay/sdk
TypeScript / Node.js
const { ZyndPay } = require('@zyndpay/sdk');

const client = new ZyndPay({
  apiKey: 'zyp_live_sk_...',
  webhookSecret: process.env.ZYNDPAY_WEBHOOK_SECRET,
});

// Create a payment
const payment = await client.payins.create({
  amount: '100.00',
  externalRef: 'order_123',
});
console.log(payment.depositAddress);

// Verify a webhook (uses raw body, not parsed JSON)
const event = client.webhooks.verify(
  rawBody,
  req.headers['x-zyndpay-signature']
);

Python SDK

The official Python SDK built on httpx with sync and async support.

pip
pip install zyndpay
Python
from zyndpay import ZyndPay

client = ZyndPay(
    api_key="zyp_live_sk_...",
    webhook_secret=os.environ.get("ZYNDPAY_WEBHOOK_SECRET"),
)

payment = client.payins.create(
    amount="100.00",
    external_ref="order_123"
)
print(payment["depositAddress"])

WooCommerce Plugin

Accept USDT payments in your WordPress / WooCommerce store without writing a single line of code. The plugin generates deposit addresses, polls confirmation status, and auto-fulfills orders.

Installation

  1. 1.Download the plugin ZIP from the ZyndPay GitHub repository.
  2. 2.In WordPress admin go to Plugins → Add New → Upload Plugin.
  3. 3.Upload the ZIP and click Activate.
  4. 4.Navigate to WooCommerce → Settings → Payments → ZyndPay.
  5. 5.Enter your API key and webhook secret, then save.

Sandbox

Overview

The sandbox lets you test your entire integration without touching real funds or the TRON network. Use your zyp_test_sk_... key — the API is identical to production.

Isolated balance

Sandbox funds are completely separate from your live balance.

Identical API

Same endpoints, same response shapes, same webhook events.

Instant confirm

Simulate payment confirmation in one API call — no waiting.

Free forever

Available to all merchants from day one, no approval needed.

Simulate Payment

Instantly confirms a sandbox payin, fires the payin.confirmed webhook, and credits the sandbox balance — exactly like a real on-chain confirmation.

POST/v1/sandbox/payin/:id/simulate
cURL
curl -X POST \
  https://api.zyndpay.com/v1/sandbox/payin/{id}/simulate \
  -H "X-Api-Key: zyp_test_sk_..."
JSON — response
{
  "success": true,
  "data": {
    "id": "cma1xyz8f0001yx5k9abc1234",
    "status": "CONFIRMED",
    "amountReceived": "100.00",
    "txHash": "sandbox_tx_abc123",
    "confirmedAt": "2026-03-06T11:00:01.000Z"
  }
}

Reference

Error Codes

All errors return the same JSON envelope. Use the error field to handle errors programmatically — the message is human-readable and may change.

JSON — error response
{
  "success": false,
  "error": "AMOUNT_TOO_LOW",
  "message": "Minimum payin amount is 20 USDT",
  "statusCode": 400,
  "requestId": "req_abc123"
}
ParameterTypeRequiredDescription
UNAUTHORIZED401optionalAPI key is missing or invalid.
FORBIDDEN403optionalAPI key does not have the required scope.
NOT_FOUND404optionalThe requested resource does not exist.
AMOUNT_TOO_LOW400optionalPayin amount is below the 20 USDT minimum.
EXTERNAL_REF_TAKEN409optionalA payin with this externalRef already exists for your account.
INSUFFICIENT_BALANCE402optionalYour balance is too low for the requested withdrawal amount.
ADDRESS_NOT_FOUND404optionalThe addressId does not match any saved withdrawal address on your account.
IDEMPOTENCY_CONFLICT409optionalIdempotency key reused with different request parameters.
RATE_LIMITED429optionalToo many requests. Retry after the Retry-After header value.
INTERNAL_ERROR500optionalUnexpected server error. Contact support if it persists.

Rate Limits

Limits are enforced per API key. Exceeded requests receive a 429 response with a Retry-After header.

ParameterTypeRequiredDescription
Default100 req / 60soptionalApplies to all endpoints per API key.
POST /v1/payin60 req / 60soptionalPayin creation is additionally rate-limited.
POST /v1/payout20 req / 60soptionalPayout creation has a stricter limit.
Tip:Enterprise merchants can request higher rate limits — contact [email protected].

© 2026 ZyndPay. All rights reserved.