> ## Documentation Index
> Fetch the complete documentation index at: https://developers.melio.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# International payments

> Paying a counterparty outside the US over the two international rails — international-fx (a foreign bank account, converted) and international-gusd (a stablecoin wallet, in dollars).

The Payouts API delivers money outside the US over two rails. They look similar in the API and are
different products underneath, so the choice is made once — when you create the receiving account —
and everything afterwards follows from it.

| | `international-fx` | `international-gusd` |
| - | - | - |
| Counterparty holds | A bank account abroad | A stablecoin wallet |
| Delivered in | The account's own currency | USD (as GUSD) |
| Converts currency | Yes | No |
| Payment payload | `CreateFxPaymentRequest` | `CreateUsdPaymentRequest` |
| `currency` on the payment | Required, the account's currency | `USD` (the default — omit it) |
| `fxQuoteToken` | Optional, locks the rate | Not accepted |
| `compliance.purpose` | **Required** | Not accepted |
| Supporting document | Sometimes required | Never required |
| Delivery preferences | `international-fx`, `fast-international-fx` | `international-gusd` |

Both rails require the same two things before you can even create the receiving account: the rail has
to be enabled for your partner (otherwise `403 INTERNATIONAL_NOT_ENABLED`), and the business has to
have completed international onboarding (otherwise `409 ENTITY_ONBOARDING_INCOMPLETE`, listing what
is outstanding). See [International onboarding](https://developers.melio.com/docs/international-onboarding).

All calls below are against `https://api.melio.com/v2`, with your `api-key` header and a
`Melio-Entity-Id` header naming the business (`ent_<uuid>`, or `me` if you have a single entity).

## 1. Create the receiving account

### international-fx

An FX account is described by its **currency**, its **destination country** and the **bank
identifiers** that corridor uses. The shapes differ per corridor — an IBAN for EUR, a sort code for
GBP, an IFSC code for INR, a CLABE for MXN, and so on — so `POST /accounts` accepts twelve variants
of `CreateExternalInternationalFxAccountRequest`. The full currency → destination → `bankDetails`
table lives in the OpenAPI description of that schema; each currency also has a ready-to-send request
example on `POST /accounts`, which is the fastest way to get the identifiers right.

```http theme={null}
POST /v2/accounts
api-key: <your key>
Melio-Entity-Id: ent_8c6f...
Idempotency-Key: 6f1c2b9e-...
Content-Type: application/json
```

```json theme={null}
{
  "ownershipType": "external",
  "type": "international-fx",
  "counterPartyName": "Studio Kepler GmbH",
  "counterPartyEmail": "ap@studiokepler.example",
  "currency": "EUR",
  "bankCountryCode": "DE",
  "bankDetails": {
    "accountHolderName": "Studio Kepler GmbH",
    "iban": "DE89370400440532013000"
  },
  "address": {
    "line1": "Torstrasse 1",
    "city": "Berlin",
    "postalCode": "10119",
    "countryCode": "DE"
  },
  "externalId": "vendor-studio-kepler"
}
```

Two things to know:

* **`USD` is not accepted on this type.** A dollar payment converts nothing, so there is no FX rail
  to put it on — use a domestic account type, or `international-gusd`. A `currency` of `USD` here is
  rejected with `400 VALIDATION_ERROR`.
* A currency outside the documented table is `400 UNSUPPORTED_CURRENCY`. Even a listed currency can
  be refused for a given destination: the international-payments service is the authority on what it
  can deliver today.

### international-gusd

A GUSD account is just a wallet:

```json theme={null}
{
  "ownershipType": "external",
  "type": "international-gusd",
  "counterPartyName": "Aurora Labs Ltd",
  "counterPartyEmail": "finance@auroralabs.example",
  "walletAddress": "9xQeWvG816bUx9EPa2rPRm2ZjV1nUwJbQ4k5s7Rm2Zj"
}
```

Only the last four characters of the wallet address are stored, and the full address is never
returned — so keep your own copy if you need to show it back to the payer.

## 2. Check the business can pay internationally

`GET /limitations` reports `payment.domestic:write` and `payment.international:write` independently.
A business may be able to pay domestically and be blocked internationally, or the reverse, so check
the international one rather than assuming:

```json theme={null}
{
  "capabilities": [
    {
      "operation": "payment.international:write",
      "allowed": false,
      "reasons": [
        {
          "code": "MissingInformation",
          "message": "Additional information is required before this business can send international payments.",
          "missingFields": ["beneficialOwners"]
        }
      ]
    }
  ]
}
```

`reasons[].code` is worth branching on: `MissingInformation` is something you can fix (see
[onboarding](https://developers.melio.com/docs/international-onboarding)), while `LocationRestricted` is not — the FX provider
will not onboard a business registered in Nevada, the US Virgin Islands, Guam or the Northern
Mariana Islands, and its reason carries an empty `missingFields` because there is no data that would
lift it. Creating a payment for a blocked business returns `403 BUSINESS_NOT_ELIGIBLE`.

## 3. Create the payment

The request body differs by rail, and `deliveryPreference` decides which one you are sending.
`POST /payments` also requires an `Idempotency-Key` header (a UUID is fine) so the call is safe to
retry — omitting it is `400 IDEMPOTENCY_KEY_REQUIRED`, and reusing one with a changed body is
`409 IDEMPOTENCY_KEY_REUSED`.

### international-fx

```json theme={null}
{
  "amount": 100000,
  "currency": "EUR",
  "originatingAccountId": "acct_3f2504e0-4f89-41d3-9a0c-0305e82c3301",
  "receivingAccountId": "acct_7d4e2a10-6c93-4f57-b8a1-2e5f0c9d4b3a",
  "deductionDate": "2026-07-10",
  "deliveryPreference": "international-fx",
  "fxQuoteToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "compliance": {
    "type": "goods-and-services",
    "purpose": "payment for consulting services"
  },
  "externalId": "invoice-2045"
}
```

* **`amount` is what the counterparty receives**, in minor units of `currency` — 100000 is €1,000.00.
  The business is debited the equivalent in USD at the applied rate, plus fees.
* **`currency` must equal the receiving account's currency.** A mismatch is `400 VALIDATION_ERROR`.
* **`fxQuoteToken` is optional.** With it you are charged exactly what was quoted; without it the
  payment is priced at creation. See [FX quotes and rates](https://developers.melio.com/docs/fx-quotes-and-rates).
* **`compliance.purpose` is required**, and a few corridors want a code rather than prose (below).
* Some destinations additionally require a supporting document — see
  [Payment attachments](https://developers.melio.com/docs/payment-attachments).

### international-gusd

```json theme={null}
{
  "amount": 125000,
  "originatingAccountId": "acct_3f2504e0-4f89-41d3-9a0c-0305e82c3301",
  "receivingAccountId": "acct_4a9d1e73-5b28-4c06-9f14-3d8e2b7a6c50",
  "deductionDate": "2026-07-10",
  "deliveryPreference": "international-gusd",
  "compliance": { "type": "goods-and-services" },
  "externalId": "invoice-2048"
}
```

GUSD is dollar-denominated, so it takes the ordinary USD payload: `currency` defaults to `USD` and
can be omitted, and there is nothing to quote and no purpose to declare. Sending `fxQuoteToken` or
`compliance.purpose` on this rail is rejected with `400 VALIDATION_ERROR` rather than accepted and
ignored — a purpose that never reaches a provider should not look like one that did.

### Payment purpose

`compliance.purpose` is what the currency provider is told about why the money is moving. It is
usually free text, but three corridors take a code from a fixed set and refuse anything else:

| Currency | Accepted values |
| - | - |
| `INR` | 10 codes, e.g. `GOODS_EXPORTED`, `SOFTWARE_EXPORT`, `TRAVEL_EXPENSES` |
| `BRL` | 7 codes, e.g. `BUY_OR_SELL_BUSINESS_SERVICE`, `INTERNATIONAL_TRAVEL` |
| `CNY` | `GOODS`, `SERVICES`, `INVESTMENTS`, `OTHER_BUSINESS` |

Send free text to one of those and the request is rejected with `400 VALIDATION_ERROR` listing the
values it accepts, so you can surface them without hard-coding the list. The provider owns these
sets: treat the table as examples and read the error for the current set.

### Delivery speed

`international-fx` is the rail's normal speed. `fast-international-fx` is the expedited variant and
is subject to a per-payment eligibility check — an ineligible request is rejected with `400`, never
silently downgraded. `POST /tools/fast-payment-eligibility` answers for the prospective payment
before you create it, and returns a decision per fast preference valid for the accounts involved,
`fast-international-fx` among them. GUSD has a single speed.

## Pricing and timing

Both rails are supported by the two estimate endpoints, and both are independent of the FX quote:

* `POST /tools/fee-calculator` — the fees for a given amount, originating account and preference.
* `POST /tools/delivery-eta` — the expected delivery date.

An FX quote covers the conversion only; fees are never inside it.

## Errors worth handling

| Code | Status | When |
| - | - | - |
| `INTERNATIONAL_NOT_ENABLED` | 403 | The rail is not enabled for your partner |
| `ENTITY_ONBOARDING_INCOMPLETE` | 409 | The business has not finished onboarding; `details` names the fields |
| `BUSINESS_NOT_ELIGIBLE` | 403 | A risk or location limitation blocks international payments |
| `UNSUPPORTED_CURRENCY` | 400 | The currency cannot be delivered |
| `VALIDATION_ERROR` | 400 | Currency mismatch, missing/invalid purpose, or a field sent on the wrong rail |
| `FX_QUOTE_INVALID` | 400 | The quote token is expired, malformed, or was issued for a different amount or currency |
| `ATTACHMENT_REQUIRED` | 400 | The destination will not accept the payment without a document |
| `INVALID_DELIVERY_PREFERENCE` | 400 | The preference's rail does not match the receiving account |

## See also

* [International onboarding](https://developers.melio.com/docs/international-onboarding) — what the business and its owners must provide first
* [FX quotes and rates](https://developers.melio.com/docs/fx-quotes-and-rates) — locking a rate versus taking the market rate
* [Payment attachments](https://developers.melio.com/docs/payment-attachments) — uploading documents and when they are required


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.