Skip to content

API

On this page

Create

Create a withdrawal from a token balance to a whitelisted wallet, choose the payout network, sign the request, and track its status.

POST/v1/withdrawals

API key: Payout

Withdrawals are irreversible once the transaction is signed and broadcast to the chain. Verify destination_address and network before you send — a request lands in created and can be cancelled only until execution starts, which may be moments later. After broadcast there is no chargeback path and a wrong address means lost funds.

Request body

Parameter Type Required Description
amount string Yes Withdrawal amount as a decimal string (e.g. "100.00")
currency string Yes Crypto asset symbol. Withdrawals are limited to USDT and USDC, each on any network where that token is issued (see Supported Currencies)
network string Yes Blockchain network code — TRC20, ERC20, BEP20, POLYGON, ARBITRUM, OPTIMISM, BASE, TON, AVAX, SOL, NEAR, SUI, or PLASMA (see Supported Currencies)
destination_address string Yes Destination wallet address. Must already be on this merchant's withdrawal whitelist
external_order_id string Yes Your payout identifier (max 200 chars), unique per merchant. Used for idempotency — a repeat with the same value returns the existing withdrawal instead of sending again

Example request

{
  "amount": "100.00",
  "currency": "USDT",
  "network": "TRC20",
  "destination_address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
  "external_order_id": "payout-001"
}

Code examples

Each tab uses an official SDK with a Payout key. Signing, serialization, safe retries, and typed errors are handled by the SDK.

import { Paymos, externalOrderId } from '@paymos/sdk';

const paymos = new Paymos({
  apiKey: process.env.PAYMOS_PAYOUT_KEY,
  apiSecret: process.env.PAYMOS_API_SECRET,
});
const withdrawal = await paymos.withdrawals.create({
  destinationAddress: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9',
  network: 'TRC20',
  currency: 'USDT',
  amount: '50.00',
  externalOrderId: externalOrderId('payout'),
});

console.log(withdrawal.withdrawalId, withdrawal.status);
import os

from paymos import Paymos

paymos = Paymos(
    api_key=os.environ["PAYMOS_PAYOUT_KEY"],
    api_secret=os.environ["PAYMOS_API_SECRET"],
)
withdrawal = paymos.withdrawals.create(
    destination_address="TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
    network="TRC20",
    currency="USDT",
    amount="50.00",
    external_order_id="payout-001",
)

print(withdrawal["withdrawal_id"], withdrawal["status"])
<?php
use Paymos\Client;
use Paymos\ClientConfig;
use Paymos\IdempotencyKey;

$paymos = new Client(new ClientConfig(
    getenv('PAYMOS_PAYOUT_KEY'),
    getenv('PAYMOS_API_SECRET')
));
$withdrawal = $paymos->withdrawals()->create(array(
    'destination_address' => 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9',
    'network' => 'TRC20',
    'currency' => 'USDT',
    'amount' => '50.00',
    'external_order_id' => IdempotencyKey::externalOrderId('payout'),
));

echo $withdrawal['withdrawal_id'] . ' ' . $withdrawal['status'];
package main

import (
	"context"
	"fmt"
	"os"

	paymos "github.com/Paymos-labs/go-sdk/v2"
)

func main() {
	client, err := paymos.NewClient(os.Getenv("PAYMOS_PAYOUT_KEY"), os.Getenv("PAYMOS_API_SECRET"))
	if err != nil {
		panic(err)
	}
	withdrawal, err := client.Withdrawals.Create(context.Background(), paymos.CreateWithdrawalParams{
		DestinationAddress: "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
		Network:            "TRC20",
		Currency:           "USDT",
		Amount:             "50.00",
		ExternalOrderID:    "payout-001",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(withdrawal.WithdrawalID, withdrawal.Status)
}
using Paymos;

using var paymos = new PaymosClient(
    Environment.GetEnvironmentVariable("PAYMOS_PAYOUT_KEY")!,
    Environment.GetEnvironmentVariable("PAYMOS_API_SECRET")!);

var withdrawal = await paymos.Withdrawals.CreateAsync(new CreateWithdrawalRequest(
    DestinationAddress: "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
    Network: "TRC20",
    Currency: "USDT",
    Amount: "50.00",
    ExternalOrderId: "payout-001"));

Console.WriteLine($"{withdrawal.WithdrawalId} {withdrawal.Status}");
import io.paymos.CreateWithdrawalRequest;
import io.paymos.PaymosClient;
import io.paymos.Withdrawal;

PaymosClient paymos = new PaymosClient(
    System.getenv("PAYMOS_PAYOUT_KEY"),
    System.getenv("PAYMOS_API_SECRET"));

Withdrawal withdrawal = paymos.withdrawals.create(new CreateWithdrawalRequest(
    "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
    "TRC20",
    "USDT",
    "50.00",
    "payout-001"));

System.out.println(withdrawal.withdrawalId() + " " + withdrawal.status());
require 'paymos'

paymos = Paymos::Client.new(
  api_key: ENV.fetch('PAYMOS_PAYOUT_KEY'),
  api_secret: ENV.fetch('PAYMOS_API_SECRET')
)
withdrawal = paymos.withdrawals.create(
  destination_address: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9',
  network: 'TRC20',
  currency: 'USDT',
  amount: '50.00',
  external_order_id: 'payout-001'
)

puts "#{withdrawal.withdrawal_id} #{withdrawal.status}"
use paymos::{CreateWithdrawalRequest, PaymosClient};

let paymos = PaymosClient::new(
    std::env::var("PAYMOS_PAYOUT_KEY")?,
    std::env::var("PAYMOS_API_SECRET")?,
)?;
let withdrawal = paymos
    .withdrawals()
    .create(&CreateWithdrawalRequest {
        destination_address: "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9".to_owned(),
        network: "TRC20".to_owned(),
        currency: "USDT".to_owned(),
        amount: "50.00".to_owned(),
        external_order_id: "payout-001".to_owned(),
    })
    .await?;

println!("{} {}", withdrawal.withdrawal_id, withdrawal.status);

Response (201 Created / 200 if idempotent match)

{
  "withdrawal_id": "wdr_2M8K6Q4P9X1Z7A3B",
  "external_order_id": "payout-001",
  "status": "created",
  "is_final": false,
  "is_test": true,
  "amount": "100.00",
  "currency": "USDT",
  "network": "TRC20",
  "destination_address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
  "created_at": 1739289600
}

Response fields

Field Type Description
withdrawal_id string Prefixed withdrawal identifier (wdr_...)
external_order_id string Your own payout reference, echoed back from the create request
status string Withdrawal status (snake_case — see Withdrawal Statuses)
is_final boolean true once the withdrawal reached a terminal state (completed, failed, cancelled)
is_test boolean true for sandbox withdrawals
amount string Withdrawal amount
fee string? Flat per-token withdrawal fee, resolved from the platform token catalog, in the same asset as amount. Fixed per token rather than a live gas quote — it covers a subsidised share of the network cost and carries no Paymos commission. Omitted when there is no fee
currency string Crypto asset symbol
network string Blockchain network code
destination_address string Destination address
tx_hash string? On-chain hash of the transfer that settled the payout. Set only once status is completed
explorer_url string? Direct link to tx_hash in the network's block explorer (e.g. Tronscan, Etherscan). Set only when tx_hash is set and the network has a configured explorer. Render verbatim as a clickable link
created_at integer Unix timestamp, seconds
completed_at integer? Unix timestamp, seconds. Set once the payout completes
failed_at integer? Unix timestamp, seconds. Set once the payout fails
cancelled_at integer? Unix timestamp, seconds. Set once the withdrawal is cancelled

Idempotency

Always send external_order_id on a withdrawal. A retry of the same call with the same external_order_id returns the existing withdrawal — never a second transfer. The response status is 200 OK (without a Location header) for an idempotent match, 201 Created for a fresh withdrawal.

This is your safety net against flaky networks, crashed workers, and retry loops. Use the same id for the same payout; pick a new id for a new one.

Errors

See Error Codes for the full catalogue. The type URI in every error response deep-links to the matching row.