Skip to main content

Foreign Currency Withdrawals (Pay Out)

This module allows you to create withdrawals (pay-out) in local currency using source funds in foreign currency (for example, USDC → MXN).

As a Merchant, your integration communicates exclusively with Pago46. Pago46 internally manages the quote, monitors the receipt of funds, and enables the cash withdrawal.

See the available pairs and countries for foreign currency, and sign every request following the Authentication guide.

Base Endpoint

All routes described below are relative to the API base URL: /api/v1

General Flow

  1. You create a quote for an asset pair (POST /merchants/quotes/).
  2. Your user accepts the quote in your application.
  3. You create a foreign withdrawal order associated with that quote.
  4. Your user sends the funds to the address indicated by Pago46.
  5. If the funding arrives within the valid window, the order advances to READY and is ready to continue the normal withdrawal flow.

Prerequisites

  • Merchant credentials (Merchant-Key and Merchant-Secret).
  • HMAC signature on every request (Message-Date, Message-Hash).
  • Public notify_url via HTTPS for status changes.
  • At least one of consumer_email or consumer_phone_number when creating the order.

Check the Authentication section for the signature.


1) Create Quote

Request a quote for the desired asset pair.

Endpoint: POST /merchants/quotes/

Request Fields

FieldTypeRequiredDescription
blockchainStringYesFunding network (SOLANA, STELLAR, MONAD).
sendDecimalYesAmount the user will send.
send_currencyStringYesSource currency (e.g., USDC).
target_countryStringYesDestination country (MX, CL, etc.).
target_currencyStringYesTarget local currency (e.g., MXN).

Request Example

curl -X POST "https://api.dev.pago46.io/api/v1/merchants/quotes/" \
-H "Merchant-Key: <YOUR_MERCHANT_KEY>" \
-H "Message-Date: <TIMESTAMP>" \
-H "Message-Hash: <HMAC_SIGNATURE>" \
-H "Content-Type: application/json" \
-d '{
"blockchain": "SOLANA",
"send": "150.00",
"send_currency": "USDC",
"target_country": "MX",
"target_currency": "MXN"
}'

Response (201 Created)

{
"id": "01952d8f-8da1-7f4b-bb36-cfba8a3be829",
"blockchain": "SOLANA",
"send": "150.00",
"send_currency": "USDC",
"target_country": "MX",
"target_currency": "MXN",
"fee": "2.00",
"fee_currency": "USDC",
"order_price": "2895.00",
"order_price_currency": "MXN",
"rate": "19.56",
"expires": "2030-01-01T11:05:00Z"
}
tip

Save the quote id. You will use it when creating the foreign order.

Quote validation

Creating a quote can fail with 400 if:

  • your account is not configured for the destination country or currency (No matching configuration found for merchant, country, and currency),
  • the pair is not valid for the blockchain (<send_currency>:<target_currency> is not a valid pair for <blockchain> blockchain),
  • the amount to send is too low (The amount to send is too low.).
Exchange-rate protection (424)

If we can't secure a fair exchange rate at that moment, the quote returns 424 with A dependent operation failed.. This is a safeguard, not a bug in your integration: we only issue a quote when the rate meets our guarantees, so your user is never left with an unfavorable rate.

It's a temporary condition and can affect one blockchain in particular while liquidity rebalances. If you hit it, retry in a few minutes; if one network keeps returning 424, offer your user another supported blockchain (SOLANA, STELLAR, MONAD) as an alternative path.


2) Create Foreign Withdrawal Order

When your user accepts the quote, create the foreign pay-out order using the quote.

Endpoint: POST /merchants/orders/pay-out/

Request Fields

FieldTypeRequiredDescription
order_typeStringYesMust be ForeignCurrencyOrder.
quoteUUIDYesID of the previously created quote.
descriptionStringYesTransaction description.
merchant_order_idStringYesUnique ID from your system per Merchant.
notify_urlURLYesURL for status notifications.
return_urlURLYesReturn URL.
expiryDateTimeYesOrder expiration (ISO 8601).
consumer_emailStringConditionalRequired if you don't send a phone number.
consumer_phone_numberStringConditionalRequired if you don't send an email.
walletStringYesWallet that will sign the on-chain transaction.

Request Example

curl -X POST "https://api.dev.pago46.io/api/v1/merchants/orders/pay-out/" \
-H "Merchant-Key: <YOUR_MERCHANT_KEY>" \
-H "Message-Date: <TIMESTAMP>" \
-H "Message-Hash: <HMAC_SIGNATURE>" \
-H "Content-Type: application/json" \
-d '{
"quote": "01952d8f-8da1-7f4b-bb36-cfba8a3be829",
"description": "Cash withdrawal from USDC balance",
"merchant_order_id": "FX-PO-2026-0001",
"notify_url": "https://your-merchant.com/webhooks/pago46",
"return_url": "https://your-merchant.com/withdrawal/back",
"consumer_email": "user@example.com",
"expiry": "2030-01-01T12:05:00Z",
"wallet": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV",
"order_type": "ForeignCurrencyOrder"
}'
Important

Do not assume a successful (201 Created) POST to /pay-out/ means the withdrawal is actually available!

  • The order has been created, but it DOES NOT INCLUDE the blockchain transaction to sign.
  • Pago46 will build the transaction asynchronously.
  • When it's ready, you will receive a webhook (CREATED status) that will include the transaction field in the order.
  • Do not attempt to sign or show blockchain data to the user until you receive this webhook!

Response (201 Created)

{
"id": "01952d91-a0ff-7f57-8f4e-68d95be01122",
"order_type": "ForeignCurrencyOrder",
"country": "MX",
"price": "2895.00",
"price_currency": "MXN",
"description": "Cash withdrawal from USDC balance",
"merchant_order_id": "FX-PO-2026-0001",
"status": "CREATED",
"notify_url": "https://your-merchant.com/webhooks/pago46",
"redirect_url": "https://checkout.dev.pago46.io/01952d91-a0ff-7f57-8f4e-68d95be01122",
"return_url": "https://your-merchant.com/withdrawal/back",
"consumer_email": "user@example.com",
"consumer_phone_number": "",
"expiry": "2030-01-01T12:05:00Z",
"paid": null,
"transaction": "",
"quote": "01952d8f-8da1-7f4b-bb36-cfba8a3be829",
"wallet": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV"
}

Notification webhook payload example

{
"id": "01952d91-a0ff-7f57-8f4e-68d95be01122",
"order_type": "ForeignCurrencyOrder",
"country": "MX",
"price": "2895.00",
"price_currency": "MXN",
"description": "Cash withdrawal from USDC balance",
"merchant_order_id": "FX-PO-2026-0001",
"status": "CREATED",
"notify_url": "https://your-merchant.com/webhooks/pago46",
"redirect_url": "https://checkout.dev.pago46.io/01952d91-a0ff-7f57-8f4e-68d95be01122",
"return_url": "https://your-merchant.com/withdrawal/back",
"consumer_email": "user@example.com",
"consumer_phone_number": "",
"expiry": "2030-01-01T12:05:00Z",
"paid": null,
"quote": "01952d8f-8da1-7f4b-bb36-cfba8a3be829",
"transaction": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADyTBSt8EsV++3pX0uVJVEfDjDp8mnxoqBQocHmFw2xTf62jiq5VGtOhOY4yZH7evGRh3UonSpGQKHYTU0EVXkCgAIBBxJXN/S1MQpq3laWmAefYKQbtIFHc3dbFnEnnIO8+VCswgaIfmL5OVR6yf1DpJxY0nPEwFUxZnKSpVis0mLyYvB+CadUk1OGINWUunRSHPvVZsHS9SxcAzkMV3l0YrJa87VfpoD7lSKyS7KXhZdFjn4tDD9PM7ZkSXL+bJrJU6WMj2H0hZpDLnPhzU1d1RhBedKdDIuLnQj09NPzfpJAUTubj9Njqmepn7FPWB/pRxyBJLcCwVALhYefbQnfHrKVYlqZDYlyLNK33Q83kGZK7y8fZH3WFOPQax4GGUN64gOvfJ3zTDIAyZPrGSRcP4/Qj+sgg0V135Yr0S5moofrpJa7uYWuPLoIHj1rtPdLNX3+MqAowL1C3mfnKmBZ/gHZOU2/2TY6lXGwBdMPZ/X4ph/o4AGrpI1iulm/FEsovQG37vM5zLFxniH7ltpr8uBf04yhJRTUvdC/74e97kFxMjiNAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwVKU1qZKSEGTSTocWDaOHx8NbXdvJK7geQfqEBBBUSNBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKkG3fbh7nWP3hhCXbzkbM3athr8TYO5DSf+vfko2KGL/LQ/+if11/ZKdMCbHylYed5LCas238ndUUsyGqezjOXo4vh36D2uxoiPQ6u1rnsxG7eMNJYIl/zlp7gsFuIP8ZcyjRWyhzKZjkXq5kHeRVDaKaLEEPfSWQM7V8ohwWOR/wYNABhBWnljbytLbWRnbTNZZFN4Q1Z5SnFBPT0OBAYYCQAKDEBCDwAAAAAABgsABQLAXBUACwAJAwQXAQAAAAAADB0OEQEJCAcEGBcMDxAMFg8ODRETFxgHEggUAgMKFSbBIJszQdacgQABAAAALwAAZAABQEIPAAAAAADskeQAAAAAABQAAA8EBBcFAQoM7vXjAAAAAAAGAWdhe29wATESFFYXVPJ+3KgjZOfKxNroQxcYyDV2rOPsBJVakZgDllyS",
"wallet": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV"
}

3) Sign and broadcast the on-chain transaction

When the webhook with the transaction field arrives, that value is the unsigned transaction that Pago46 already built. The format depends on the quote's network (base64, XDR, JSON, etc.) — see the per-network detail below. Your user must sign it with the wallet you provided when creating the order and broadcast it to the network to deposit the funds to the Pago46 address.

The procedure is each network's standard one; you don't need any Pago46-specific logic:

  1. Decode/parse the transaction field according to the network's format (see detail below).
  2. Deserialize it with the corresponding blockchain SDK.
  3. Sign it with the user's wallet.
  4. Broadcast it to the network and wait for confirmation.

Once the transaction is confirmed on-chain and Pago46 validates the receipt of funds, the order advances to READY and we notify you via webhook.

By the quote's network:

  • Solana — the transaction is a VersionedTransaction. Deserialize it from base64, sign it with the wallet keypair and send it through a Solana RPC. See @solana/web3.js and the Solana docs.
  • Stellar — the transaction is an XDR TransactionEnvelope. Decode it, sign it with the account key and submit it to Horizon. See the Stellar JS SDK and the Stellar docs.
  • Monad — it is EVM-compatible. The transaction field arrives as a JSON object with the transaction parameters (not base64). Pass it to your usual library (ethers.js or viem) to sign and broadcast it like any Ethereum transaction. See the Monad docs.
Sandbox uses testnet; Production uses mainnet

Sandbox runs on the testnet of all three blockchains (Solana, Stellar and Monad); Production runs on mainnet. Point the wallet and the RPC to the correct network for each environment. See Environments for each one's base URLs.

Test token in Sandbox

In Sandbox we use a non-mintable test token for the source assets (for example, USDC). To test on testnet you need us to fund you with that token: request it from your commercial executive or email contacto@pago46.com.


4) Retrieve Order

You can retrieve the withdrawal order whenever you need to verify its status.

Endpoint: GET /merchants/orders/pay-out/{id}/

curl -X GET "https://api.dev.pago46.io/api/v1/merchants/orders/pay-out/01952d91-a0ff-7f57-8f4e-68d95be01122/" \
-H "Merchant-Key: <YOUR_MERCHANT_KEY>" \
-H "Message-Date: <TIMESTAMP>" \
-H "Message-Hash: <HMAC_SIGNATURE>"

Order Statuses (Foreign Pay-Out)

As a merchant you only need to react to the states we notify; the intermediate cash-handout step is resolved internally.

StatusNotified via webhook?What does it mean for the merchant?
CREATEDOrder creation responseThe order is registered and contains the information to sign for the on-chain send
READYYesWe confirmed receipt of the funds on-chain in the Pago46 wallet; the order proceeds
PAYMENT_STARTEDNo (internal)Intermediate step: a provider is handing the cash to the beneficiary
COMPLETEDYes — final stateThe withdrawal completed; the beneficiary received the cash
CANCELLEDYes — final stateThe order was cancelled by the Pago46 team (neither the merchant nor the user can cancel) and will not be processed
EXPIREDYes — final stateThe order expired and will not be processed

State Transition Diagram


Integration Best Practices

  • Use idempotent merchant_order_id per business operation.
  • Handle status changes in a retry-tolerant manner.
  • Do not assume COMPLETED immediately after creating the order.
  • Show the user a countdown based on the quote window.
  • After a POST to /pay-out/, maintain a status like "Waiting for transaction" until you receive the webhook containing the transaction field. Only then proceed to collect a signature from the user or advance the flow.
  • Store the returned order id so you can correlate the initial POST and the webhook notification.
  • In case of validation errors, always request a new quote before retrying. See Common Errors for the exact error formats returned by the API for validation and logic issues.

If you need support for new asset pairs (for example, additional source currencies or destination countries), contact your commercial executive or email contacto@pago46.com.