Sending payouts

Payouts in depth — destinations, mandatory idempotency, approval flows, and reconciling the result.

A payout is money out to your end user. OmiCloud creates the transaction and submits it to the gateway, which sends the funds from its own account to the destination you supply. There is no hosted page; the whole flow is server to server.

your app ──POST /v1/payouts──▶ OmiCloud ──▶ gateway ──sends──▶ end user's address
                                              │
                                              └──callback──▶ OmiCloud ──webhook──▶ you

Before you call

  • Approve first, then call. OmiCloud does not hold or review payouts. By the time you call POST /v1/payouts you should have done your own checks (balance, limits, fraud, KYC). The gateway may apply its own review on top — that shows as gatewayState: PENDING_APPROVAL while state stays processing.
  • Check the route. GET /v1/routes?direction=payout&currency=USDT gives you limits and which networks are available for sending.

The request

curl -X POST https://api.omicloud.io/v1/payouts \
  -H "Authorization: Bearer omi_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wd-20260902-4413" \
  -d '{
    "amount": "250",
    "currency": "USDT",
    "network": "TRX",
    "destination": { "type": "crypto_address", "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE" },
    "endUserId": "player-8812",
    "operatorReference": "wd-20260902-4413",
    "description": "Withdrawal #4413"
  }'

Idempotency-Key is required. A payout without one is rejected with 400 VALIDATION_FAILED — the risk of paying twice on a retry is too high to make it optional. Use your own withdrawal id. See Idempotency.

Destinations

destination is a typed object so new rails can be added without changing existing callers:

typeFieldsAvailable
crypto_addressaddress, optional tag (memo / destination tag where the chain needs one)now
bank_account—planned; will be announced in the changelog

OmiCloud validates the address format for the network and returns 400 VALIDATION_FAILED on a mismatch — for instance an 0x… address with network: TRX. It cannot tell whether the address is the right one; that is between you and your end user.

The response

{ "transaction": "txn_2b7d9e4f11a0", "state": "processing", "gateway": "dce", "gatewayReference": "wd_7f1c2a" }

201 means the gateway accepted the payout, not that it has been sent. Debit the end user's balance now (you already reserved it during approval); release the reservation if it later declines.

Tracking

The payout settles when the gateway confirms the transfer — for crypto, when the transaction has enough confirmations. You receive transaction.settled, and GET /v1/transactions/{reference} then has crypto.txHash. Show the hash to the end user; it is their proof.

If it fails you receive transaction.declined. Typical declineReason values for payouts:

declineReasonWhat happenedWhat to do
gateway_rejectedThe gateway refused it; gatewayState and gatewayDetails in the console say why.Investigate; usually a compliance hold or a rejected address.
invalid_destinationThe gateway could not send to this address.Ask the end user to check it.
insufficient_fundsThe gateway account could not cover it. OmiCloud is alerted.Retry later with a new idempotency key.
gateway_unreachableOmiCloud could not reach the gateway when submitting.Safe to retry with a new key.

A declined payout never sent money. Return the reserved balance to the end user.

Timing expectations

NetworkTypical time to settled
TRX1–3 minutes
BNB1–3 minutes
ETH3–10 minutes, longer under congestion
SOLunder 1 minute

A payout still processing after an hour is unusual. Check gatewayState — PENDING_APPROVAL means the gateway is reviewing it manually. OmiCloud's own monitoring flags stuck payouts; you do not need to escalate unless the end user is waiting.

Reconciling

Once a day, list settled payouts and compare against your ledger:

GET /v1/transactions?direction=payout&state=settled&since=<yesterday>&limit=100

Match on operatorReference. Any settled payout you do not have a record of is worth a support ticket with its reference.


Did this page help you?