En esta página
Crear
Crea un retiro desde un saldo en tokens hacia una billetera de la lista blanca, elige la red de salida, firma la petición y sigue su estado.
Clave de API: Payout
Un retiro es irreversible en cuanto la transacción se firma y se difunde a la cadena. Verifica destination_address y network antes de enviar: la petición aterriza en created y solo puede cancelarse hasta que arranca la ejecución, algo que puede ocurrir instantes después. Tras la difusión no hay vía de contracargo, y una dirección equivocada significa fondos perdidos.
Un retiro no espera a ninguna tanda: la ejecución arranca en cuanto se acepta la petición. Lo que tarde luego la transferencia en llegar es la velocidad de la red de salida con la carga que tenga en ese momento.
Cuerpo de la petición
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
amount |
string | Sí | Importe del retiro como cadena decimal (por ejemplo, "100.00") |
currency |
string | Sí | Símbolo del activo cripto. Se puede retirar cualquier activo del que tengas saldo; las redes disponibles dependen del activo (consulta Monedas admitidas) |
network |
string | Sí | Código de la red de salida: TRC20, ERC20, BEP20, POLYGON, ARBITRUM, OPTIMISM, BASE, TON, AVAX, SOL o PLASMA. Son once de las trece redes que aceptan pagos: NEAR y SUI solo cobran, y una dirección suya no entra en la lista blanca (consulta Monedas admitidas) |
destination_address |
string | Sí | Dirección de la billetera de destino. Debe estar ya en la lista blanca de retiros de este comercio |
external_order_id |
string | Sí | Tu identificador de envío (máximo 200 caracteres), único por comercio. Sirve para la idempotencia: una repetición con el mismo valor devuelve el retiro existente en lugar de enviar de nuevo |
Petición de ejemplo
{
"amount": "100.00",
"currency": "USDT",
"network": "TRC20",
"destination_address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
"external_order_id": "payout-001"
}
Ejemplos de código
Cada pestaña usa un SDK oficial con una clave Payout. La firma, la serialización, los reintentos seguros y los errores tipados los resuelve el SDK.
API_KEY_ID="rk_live_xxxxxxxxxxxx"
API_SECRET="sk_live_xxxxxxxxxxxx"
BODY='{"destination_address":"TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9","network":"TRC20","currency":"USDT","amount":"50.00","external_order_id":"payout-123"}'
TS=$(date +%s)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" POST /v1/withdrawals '' "$BODY_HASH" \
| openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)
curl -sS https://api.paymos.io/v1/withdrawals \
-H "Authorization: HMAC-SHA256 $API_KEY_ID:$SIGNATURE" \
-H "X-Request-Timestamp: $TS" \
-H "Content-Type: application/json" \
-d "$BODY"
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);
Respuesta (201 Created / 200 si hay coincidencia idempotente)
{
"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
}
Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
withdrawal_id |
string | Identificador de retiro con prefijo (wdr_...) |
external_order_id |
string | Tu propia referencia de envío, devuelta tal cual desde la petición de creación |
status |
string | Estado del retiro (snake_case; consulta Estados del retiro) |
is_final |
boolean | true cuando el retiro ha alcanzado un estado final (completed, failed, cancelled) |
is_test |
boolean | true en retiros de Sandbox |
amount |
string | Importe del retiro |
fee |
string? | Comisión de red fija por token, resuelta desde el catálogo de tokens de la plataforma, en el mismo activo que amount. Es fija por token en lugar de una cotización de gas en vivo: se fija por debajo del coste de red de esa ruta y no lleva comisión de Paymos. Se omite cuando no hay comisión |
currency |
string | Símbolo del activo cripto |
network |
string | Código de la red blockchain |
destination_address |
string | Dirección de destino |
tx_hash |
string? | Hash en cadena de la transferencia que liquidó el envío. Solo se rellena cuando status es completed |
explorer_url |
string? | Enlace directo a tx_hash en el explorador de bloques de la red (por ejemplo, Tronscan o Etherscan). Solo se rellena cuando hay tx_hash y la red tiene un explorador configurado. Muéstralo tal cual como enlace pulsable |
created_at |
integer | Marca temporal Unix, en segundos |
completed_at |
integer? | Marca temporal Unix, en segundos. Se rellena cuando el envío se completa |
failed_at |
integer? | Marca temporal Unix, en segundos. Se rellena cuando el envío falla |
cancelled_at |
integer? | Marca temporal Unix, en segundos. Se rellena cuando el retiro se cancela |
Idempotencia
Envía siempre external_order_id en un retiro. Repetir la misma llamada con el mismo external_order_id devuelve el retiro existente, nunca una segunda transferencia. El estado de la respuesta es 200 OK (sin cabecera Location) en una coincidencia idempotente y 201 Created en un retiro nuevo.
Es tu red de seguridad frente a redes inestables, procesos caídos y bucles de reintento. Usa el mismo identificador para el mismo envío y uno nuevo para uno distinto.
Errores
Consulta Códigos de error para el catálogo completo. La URI de type en cada respuesta de error enlaza directamente con la fila correspondiente.