Currencies, rails and providers
Fiat money on this API moves through two banking providers — Fiat Republic and
OpenPayd — and every rail field you will meet (rail on account create, on beneficiary
create, on withdrawal create and on payout batches) means something slightly different. This
page is the one table for all of them, plus the rule that ties an account, its beneficiaries and
its withdrawals to one provider.
Currencies
The fiat surface supports GBP, EUR and USD. Fiat account create, beneficiary create and
update, and fiat withdrawal create all refuse anything else with 422 unsupported_currency
(payout batches enforce their own currency rule — see that route). Conversions use a different
list: GBP, USD and EUR plus BTC, ETH, USDC and USDT — see Conversions.
Two refusals sit ahead of everything: a customer in a blocked jurisdiction gets 403
fiat_unsupported_jurisdiction on account create for every currency, and a currency not
offered to the customer's type gets 403 currency_not_available_for_customer_type.
Which provider holds an account
| Currency | Fiat Republic | OpenPayd |
|---|---|---|
| GBP | Yes | Yes |
| EUR | Yes | Yes |
| USD | Yes | No — OpenPayd issues GBP and EUR only, so a USD account is always Fiat Republic. |
You do not pick OpenPayd. POST .../banking/accounts accepts an explicit provider of
FIAT_REPUBLIC only (422 unsupported_provider for anything else, OPENPAYD included); when
you omit provider, the tenant's routing rules choose, and that is the only way an account
is placed with OpenPayd. GET .../banking/accounts/form-schema describes what can be named — it
therefore advertises Fiat Republic alone, while its currency and rail lists describe what the
platform can do.
Read the account's provider field to learn where it landed. It decides everything downstream.
One account per currency. A customer may hold at most one active fiat account per currency
(Pending, PendingProvider, ManualReview or Completed), at any provider. A second request
in the same currency at the same provider returns the existing account; at a different provider
it is refused with 409 duplicate_currency_account — the detail names the provider and status
of the account in the way. To move a currency to the other bank, the existing account has to be
closed first.
The provider chain
Provider identity is a chain, not a per-call choice:
- Account — issued at Fiat Republic or OpenPayd (above).
- Beneficiary —
POST .../banking/beneficiariescreates the payee at each provider where the customer holds an account in that currency that the provider has accepted (provider_account_idset). No such account → 409bank_account_required. Namingprovidernarrows the choice to that provider's accounts; it cannot reach a provider the customer has no account with. - Withdrawal —
POST .../banking/withdrawalsis funded from the customer'sCompletedaccount at the beneficiary's provider in the withdrawal currency. If there is none (for example the account was routed to OpenPayd but the beneficiary is an old Fiat Republic row, or the account is notCompletedyet) the call is refused with 409source_account_not_ready.
The practical consequence: check provider on the account first, then create beneficiaries
only after provider_account_id is set, and expect beneficiaries and withdrawals to follow the
account's provider. Details of step 2 are in Beneficiaries.
Identifiers a receiving bank can recognise (OpenPayd EUR/GBP). destination on a withdrawal
row is the provider's own internal reference — never quote it to a bank. Once an OpenPayd payout
is accepted, the withdrawal read also carries transaction_id (the provider's own transaction
id), end_to_end_reference (the SEPA/SWIFT end-to-end reference actually quoted to the receiving
bank) and provider_short_id (a short, human-readable reference such as 20220922-K3QY65) — use
provider_short_id when reading a reference out over the phone, and transaction_id when
escalating to support. All three are null until the provider accepts the payment, null for a
provider that does not report one, and null on rows created before this field shipped (except
transaction_id, which is recovered from the stored OpenPayd reference where possible).
Rails
Two vocabularies exist, one per provider:
- Fiat Republic uses scheme codes:
FPS,CHAPS,BACS(GBP);SCT(EUR);ACH,ACH_SAME_DAY,DOMESTIC_WIRE,INTERNATIONAL_WIRE(USD). Input is normalised — case, hyphens and spaces are tolerated andFASTER_PAYMENTS,SEPA,SEPA_INSTANT,WIRE,FEDWIRE,SWIFTare accepted as aliases — but only the canonical code is stored and echoed. - OpenPayd uses payment types:
FASTER_PAYMENTS,CHAPS(GBP);SEPA,SEPA_INSTANT(EUR).FPSis accepted forFASTER_PAYMENTS;SCTis accepted and means standardSEPA, notSEPA_INSTANT. - OpenPayd EUR and GBP over SWIFT: explicit international transfers, enabled separately
for each currency and verified bank destination. SWIFT is never a default. Obtain the available
destinations from
GET /api/v2/dashboard/banking/swift-options(or the equivalent partner customer route). Create an OpenPayd beneficiary withrail=SWIFT, the recipient bank country, exactly one IBAN or account number as specified by that destination, a BIC and the beneficiary's own address. The optional intermediary field is a BIC only; additional routing codes, bank or intermediary addresses and address state are not supported on this route. Combined address lines may contain at most 255 characters.purpose_codeis not exposed by the withdrawal API. SWIFT beneficiaries cannot be paid through a domestic rail or a payout batch. - Before sending SWIFT, obtain
GET /api/v2/dashboard/banking/swift-quote?beneficiaryId=...&amountMinor=...(or the equivalent partner customer route). Submit itsfeeMinorStringasexpectedSwiftFeeMinorand itsfeeConfigurationVersionIdasswiftFeeConfigurationVersionId. The v1 request usesexpected_swift_fee_minorandswift_fee_configuration_version_id. A new request with a changed fee is refused before a payment is claimed. A timeout must be retried with the same idempotency key and entire original payload, including the accepted fee version. Do not obtain a new quote for an unresolved request. Expect 2–5 working days; intermediary or receiving banks may deduct their own charges from the amount received.
Which one applies is decided by the provider of the beneficiary (withdrawals) or of the routed account (accounts), never by the value you send.
| Currency | Fiat Republic withdrawal / payout-batch rails | Default when rail is omitted (Fiat Republic) |
OpenPayd withdrawal rails | Default when omitted (OpenPayd) |
|---|---|---|---|---|
| GBP | FPS, CHAPS, BACS |
FPS |
FASTER_PAYMENTS, CHAPS, SWIFT when enabled |
FASTER_PAYMENTS |
| EUR | SCT |
SCT |
SEPA, SEPA_INSTANT, SWIFT when enabled |
SEPA_INSTANT |
| USD | ACH, ACH_SAME_DAY, DOMESTIC_WIRE, INTERNATIONAL_WIRE |
ACH, unless the payee's confirmed schemes are known, in which case the first of ACH, ACH_SAME_DAY, DOMESTIC_WIRE, INTERNATIONAL_WIRE it supports |
— (no USD at OpenPayd) | — |
What each route does with rail:
| Route | Meaning of rail |
Validated? |
|---|---|---|
POST .../banking/accounts |
A provider-routing selector, matched against the tenant's routing rules; stored on the account, ignored by the account adapters. Omitting it substitutes the Fiat Republic default (FPS / SCT / ACH) before routing, so a rule keyed on an OpenPayd code is reached only by sending that code. |
No — never against a scheme list. |
POST .../banking/beneficiaries |
On USD it selects the payee shape and routing-code type; use schemes to register several USD schemes. On OpenPayd EUR/GBP, explicit SWIFT selects the international beneficiary contract above. |
SWIFT: currency, destination and fields checked before creation. |
POST .../banking/withdrawals |
The scheme the payment goes out on, in the beneficiary provider's vocabulary (table above). Fiat Republic: 422 unsupported_rail outside the currency's set, 422 rail_not_supported_by_beneficiary when the payee's confirmed USD schemes exclude it, both before any funds are reserved. OpenPayd: translated to its payment type; a value it cannot translate fails after the hold and returns 200 with status Failed. |
Fiat Republic: yes. OpenPayd: at the adapter. |
POST .../payout-batches |
Rail for every item, validated against the Fiat Republic scheme set for the batch currency (422 unsupported_rail) and stored as the canonical code — you send FASTER_PAYMENTS, you read back FPS. |
Yes. |
Per-rail limits and scheme notes
- OpenPayd Faster Payments is capped at GBP 1,000,000 per payment. An amount above it is
not refused: the withdrawal returns 200 with
statusFailedand the funds released. UseCHAPS, which has no cap, for larger GBP payments. - EUR at OpenPayd:
SCTandSEPAselect standard SEPA Credit Transfer; omitting the rail selectsSEPA_INSTANT. SendSEPA_INSTANTexplicitly if that is what you want. - Payment references (
descriptionon a withdrawal) are checked before the hold for USD at Fiat Republic and supported OpenPayd payment types. OpenPayd requires ASCII and a byte cap of 18 for Faster Payments, 35 for CHAPS/SWIFT and 140 for SEPA. Invalid references return 422. - OpenPayd EUR/GBP SWIFT preflight checks the enabled route, stored beneficiary, owned source
account and accepted fee before a new payment is claimed or held. A preflight refusal returns
422
swift_preflight_failed; an idempotent replay retains the original payment and fee. - Inbound: money reaches an issued account as a credit transfer and lands as a
deposit.receivedwebhook. SEPA Direct Debit is not supported on EUR accounts.
Conversions
POST .../banking/conversion-requests converts between the customer's balances. The asset
list is GBP, USD, EUR, BTC, ETH, USDC and USDT, with three rules:
from_currencyandto_currencymust differ.- A BTC or ETH source converts to fiat only — BTC→USDC, ETH→USDT, BTC→ETH and the like are
refused with 422
invalid_pair. Fiat, USDC and USDT sources may target any other listed asset. - Fiat, USDC and USDT sources have a 20 major-unit minimum (422
below_minimum_trade_size).
The customer must already hold a Completed fiat account (fiat side) or an active wallet
(crypto side) for both assets — 409 exchange_account_missing / exchange_wallet_missing
otherwise; this route opens neither.
GET /api/v1/partner/transfer/allowed-destinations lists the assets the exchange can deliver for
your tenant; it is derived from the tenant's exchange fee configuration and is empty when no
exchange fee is configured. Pass source_currency to have the pair rules applied for you.
GET /api/v1/partner/transfer/price is indicative only: the conversion executes at the live
rate and references nothing from the quote.