API reference

Payouts

Pay out from your app pool to any wallet address. External wallets are paid on-chain; wallets on our network are settled internally — instantly and without a network fee.

Pay out to any address

Send the destination address (to_address) and an amount. When a customer asks to withdraw, pay straight to the address they entered. Put your own reference on every payout so that a retry can never pay twice.

The destination decides the route

External wallet

201 · PENDING

Paid on-chain. The response is 201 with status PENDING, which becomes SUCCESS once the transaction is confirmed. The asset's network fee (withdrawal_fee in total-balance) is taken from the pool together with the amount.

Wallet on our network

201 · SUCCESS

Settled internally — instantly, with no network fee. The response is 201 with status SUCCESS. Your app's internal transfer fee applies.

Addresses that can't receive

400 · INVALID_ADDRESS

Reserved addresses and customer deposit addresses — your own customers' or another app's — can't be paid. The request fails with INVALID_ADDRESS.

Endpoint

POST/api/partner/{app_id}/transfer/

Pays from the pool to to_address and returns the payout object. POST /withdrawals/ is an alias that behaves the same way.

Path parameters

  • app_idstringrequired
    Your app ID (UUID), shown on the app's Developer tab.

Body parameters

  • to_addressstringrequired
    Destination Solana address — an external wallet or a wallet on our network.
  • amountdecimal stringrequired
    Amount as a positive decimal string. Up to 6 decimals for USDT and 9 for SOL.
  • referencestring
    Your payout reference — recommended, unique per app, up to 128 characters. Sending the same reference again returns the original result without paying again.
  • asset_idinteger
    Asset ID. Optional — defaults to USDT on Solana. SOL and USDT are supported.
  • user_idstring
    Customer user_id. Optional — records which customer the payout is for. It doesn't debit the customer's balance; use balance/adjust for that.

Request example

curl -X POST "https://api.moallet.com/api/partner/{app_id}/transfer/" \
  -H "Authorization: ApiKey {client_id}:{client_secret}" \
  -H "Content-Type: application/json" \
  -d '{
    "to_address": "5DmYQC3fLxHUpmYbYuRkHFQ2yH4H3kP3VpYWgPMbpnGk",
    "amount": "50",
    "reference": "payout-20261022-0001",
    "user_id": "customer-1001"
  }'

Response — external wallet (on-chain)

201 Created
{
  "success": true,
  "data": {
    "id": "2a1f7a03-9b2b-4fa1-9f77-33f8a09e8a12",
    "asset": 2,
    "asset_symbol": "USDT",
    "network_name": "Solana Mainnet",
    "amount": "50.000000000000000000",
    "to_user_id": "customer-1001",
    "error_message": "",
    "created_at": "2026-10-22T10:15:22Z",
    "reference": "payout-20261022-0001",
    "to_address": "5DmYQC3fLxHUpmYbYuRkHFQ2yH4H3kP3VpYWgPMbpnGk",
    "status": "PENDING",
    "tx_hash": "",
    "fee": "1.000000000000000000",
    "processed_at": null,
    "is_external_payout": true
  },
  "error": null
}

Response — wallet on our network (instant)

201 Created
{
  "success": true,
  "data": {
    "id": "5c0d1e44-0f6a-4b1e-a3c2-9d1f0b7e2c55",
    "asset": 2,
    "asset_symbol": "USDT",
    "network_name": "Solana Mainnet",
    "amount": "50.000000000000000000",
    "to_user_id": "customer-1001",
    "error_message": "",
    "created_at": "2026-10-22T10:15:22Z",
    "reference": "payout-20261022-0001",
    "to_address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
    "status": "SUCCESS",
    "tx_hash": "internal:5c0d1e44-0f6a-4b1e-a3c2-9d1f0b7e2c55",
    "fee": "1.000000000000000000",
    "processed_at": "2026-10-22T10:15:22Z",
    "is_external_payout": false
  },
  "error": null
}

Payout status

PENDINGAccepted, waiting to be sentPROCESSINGSent, waiting for confirmationSUCCESSPaidFAILEDFailed — the amount and fee go back to the pool

A 201 response isn't final for on-chain payouts. Poll the payout until it is SUCCESS or FAILED:

curl "https://api.moallet.com/api/partner/{app_id}/withdrawals/{withdrawal_id}/" \
  -H "Authorization: ApiKey {client_id}:{client_secret}"

72-hour hold on internal inflows

Funds that reach your app by internal settlement from other wallets on our network can't be paid out to external wallets for 72 hours after they arrive. On-chain deposits to customer addresses and pool top-ups can be paid out right away. payout_available in total-balance shows what you can pay out now.

Errors

Errors return success: false with an error code.
  • INVALID_ADDRESS400
    Invalid address, or an address that can't receive.
  • INVALID_AMOUNT400
    The amount isn't positive or has too many decimals.
  • INVALID_REFERENCE400
    reference is longer than 128 characters.
  • UNSUPPORTED_ASSET400
    Unsupported asset — SOL and USDT only.
  • INSUFFICIENT_POOL400
    The pool holds less than amount + fee.
  • WITHDRAWAL_LOCKED400
    Part of the pool is on the 72-hour hold. data includes locked, available and next_unlock_at.
  • DUPLICATE_REFERENCE_MISMATCH409
    The reference was already used with different details (amount, address …).
  • ADDRESS_IS_TOKEN_ACCOUNT400
    The destination is a token account or the token's mint address — send to the owner wallet address.
  • ADDRESS_NOT_ALLOWLISTED403
    The payout allowlist is on and the destination isn't an active address on it (or it's still in its 24-hour activation period).
  • READ_ONLY_KEY403
    The request was made with a read-only API key.
  • PARTNER_KYC_REQUIRED403
    The account behind this app hasn't completed verification.
  • PAYOUT_DISABLED403
    On-chain payouts are paused for this app. Contact your account manager.
400 Bad Request
{
  "success": false,
  "data": {
    "requested_total": "101",
    "pool": "120.8",
    "locked": "40",
    "available": "80.8",
    "next_unlock_at": "2026-10-25T03:12:00+00:00"
  },
  "error": {
    "code": "WITHDRAWAL_LOCKED",
    "message": "Part of the pool is still in the 72-hour hold."
  }
}

Response fields

Payout object

  • idstring (UUID)
    Payout id (UUID).
  • statusstring
    Payout status (see above).
  • is_external_payoutboolean
    true for an on-chain payout, false for internal settlement.
  • referencestring | null
    The reference you sent (null if none).
  • amountstring
    Amount paid, in asset units.
  • feestring
    Fee taken — the network fee on-chain, or your app's internal transfer fee.
  • to_addressstring
    Destination address.
  • to_user_idstring
    Customer user_id recorded with the payout.
  • tx_hashstring
    The on-chain signature, or an internal: reference for internal settlement.
  • asset · asset_symbol · network_nameinteger · string · string
    Asset id, symbol and network name.
  • error_messagestring
    Why the payout failed (status FAILED).
  • created_atstring (ISO 8601)
    When the payout was requested.
  • processed_atstring | null
    When it finished — at confirmation on-chain, immediately for internal settlement.

Restrict destinations with the payout allowlist — see Security controls.

List and look up payouts with the payout history API.