Multicurrency
Multi-currency guide
Run GBP and EUR accounts side by side. Understand the differences between payment rails, manage balances across currencies, and build integrations that handle both.
How Mercury handles currencies
Mercury supports two currencies: GBP and EUR. Each has its own account type, payment rail, and set of endpoints. A single client can hold one account per currency, managed independently but queryable together.
/api/v1/sterling-account/api/v1/payments (Faster Payments)GB/api/v1/euro/vibans/api/v1/euro/payments/sepa-ct (SEPA CT)MCCY (multi-currency)Account structure per client
Each client can hold one GBP and one EUR virtual IBAN. Both are provisioned independently and have separate balances. Use the client accounts endpoint to retrieve both in a single call.
GB71CLRB04082700001159
Sort code: 040827 | Acc: 00001159
GB85CLRB04274893940059
Sort code: 042748 | Acc: 93940059
EUR accounts also carry a GB-prefixed IBAN (e.g. GB85CLRB...). This is correct for UK-based accounts holding EUR. Do not reject these in your IBAN validation. Differentiate by the Currency field in the response.
Provisioning both accounts
Create accounts sequentially or in parallel. Both use the same request body. Mercury determines the currency from the endpoint path.
import requests
# GBP account
gbp_response = requests.post(
f"{BASE_URL}/api/v1/sterling-account",
headers=headers,
json={"clientReference": "CLIENT-001"}
)
# EUR account
eur_response = requests.post(
f"{BASE_URL}/api/v1/euro/vibans",
headers=headers_eur, # re-sign for different path
json={"clientReference": "CLIENT-001"}
)
print("GBP:", gbp_response.json())
print("EUR:", eur_response.json())// GBP account
const gbpRes = await fetch(`${BASE_URL}/api/v1/sterling-account`, {
method: "POST",
headers: signHeaders("POST", "/api/v1/sterling-account", body),
body: JSON.stringify({ clientReference: "CLIENT-001" })
});
// EUR account
const eurRes = await fetch(`${BASE_URL}/api/v1/euro/vibans`, {
method: "POST",
headers: signHeaders("POST", "/api/v1/euro/vibans", body),
body: JSON.stringify({ clientReference: "CLIENT-001" })
});Both return Status: "Processing" immediately. Listen for Accounts.AccountCreated webhooks to confirm each IBAN is ready. The EUR creation also fires Clients.ClientAssignedAccount.
Querying across currencies
Three ways to query accounts, depending on what you need:
GET /api/v1/sterling-account
Every GBP account across all your clients. Use for balance monitoring dashboards.
GET /api/v1/euro/vibans
Every EUR account across all your clients. Same response structure as GBP.
GET /api/v1/client/{ref}/accounts
GBP + EUR in one call. Best for client detail pages and per-client reconciliation.
[
{
"ClientReference": "CLIENT-001",
"ClientName": "Acme Ltd",
"Iban": "GB85CLRB04274893940059",
"SortCode": "042748",
"AccountNumber": "93940059",
"Balance": { "CurrencyCode": "EUR", "Amount": 1000 },
"AvailableBalance": { "CurrencyCode": "EUR", "Amount": 1000 },
"Currency": "EUR",
"Status": "Active",
"ExternalReference": "EXT-REF-001",
"CreatedDate": "2026-02-23T15:26:37Z"
},
{
"ClientReference": "CLIENT-001",
"ClientName": "Acme Ltd",
"Iban": "GB71CLRB04082700001159",
"SortCode": "040827",
"AccountNumber": "00001159",
"Balance": { "CurrencyCode": "GBP", "Amount": 1000 },
"AvailableBalance": { "CurrencyCode": "GBP", "Amount": 1000 },
"Currency": "GBP",
"Status": "Active",
"ExternalReference": "EXT-REF-001",
"CreatedDate": "2026-02-23T15:26:37Z"
}
]Payment rails comparison
GBP and EUR use completely separate payment infrastructure. The endpoint, request format, operating hours, and webhook events all differ.
Sending GBP payments
Submit via Faster Payments. Provide the creditor by IBAN or by account number + sort code. Optionally link a Confirmation of Payee check.
{
"reference": "CLIENT-001",
"endToEndIdentifier": "E2E-20260414-001",
"debtor": {
"iban": "GB71CLRB04082700001159",
"paymentAmount": {
"currencyCode": "GBP",
"amount": 500
},
"paymentReference": "Invoice 2026-001"
},
"creditor": {
"accountName": "John Smith",
"accountNumber": "55779911",
"sortCode": "200000"
}
}{
"Payment": {
"PaymentId": "764b7d22-9fa0-43bb-920e-326e8634b0f8",
"Reference": "CLIENT-001",
"Debtor": {
"Iban": "GB71CLRB04082700001159",
"PaymentAmount": { "CurrencyCode": "GBP", "Amount": 500 },
"PaymentReference": "Invoice 2026-001",
"Name": "Acme Ltd"
},
"Creditor": {
"AccountName": "John Smith",
"AccountNumber": "55779911",
"SortCode": "200000",
"Iban": "GB60BARC20000055779911"
},
"Status": "Pending",
"Message": "Payment has been successfully requested and is now being processed"
}
}Sending EUR payments
Submit via SEPA Credit Transfer. The creditor must have an IBAN. If no IBAN is available, provide a SWIFT/BIC code instead.
SEPA payments are only processed Monday to Friday, 06:00 to 14:25 UK time. Submissions outside this window or on TARGET2 holidays (1 January, 1 May, 25-26 December) will be rejected.
{
"clientReference": "CLIENT-001",
"endToEndIdentifier": "E2E-EUR-20260414-001",
"debtor": {
"iban": "GB85CLRB04274893940059",
"paymentAmount": {
"currencyCode": "EUR",
"amount": 1000
},
"paymentReference": "Invoice EUR-2026-001"
},
"creditor": {
"accountName": "Sender GmbH",
"iban": "FR7630006000011234567890189"
}
}{
"Payment": {
"PaymentId": "085440ed-1326-41f4-b4a6-6188dbd42409",
"Reference": "CLIENT-001",
"Debtor": {
"Iban": "GB85CLRB04274893940059",
"PaymentAmount": { "CurrencyCode": "EUR", "Amount": 1000 },
"PaymentReference": "Invoice EUR-2026-001",
"Name": "Acme Ltd"
},
"Creditor": {
"AccountName": "Sender GmbH",
"AccountNumber": null,
"SortCode": null,
"Iban": "FR7630006000011234567890189"
},
"Status": "Pending",
"Message": "Payment has been successfully requested and is now being processed"
}
}Returning GBP payments
You can return a received inbound FPS payment to the sender. Provide the original transaction ID and a return reason. This is only available for GBP.
{
"transactionId": "TXN-1234",
"returnReason": "Funds received in error"
}{
"Message": "Return payment successfully submitted."
}Webhook events by currency
Account webhooks are shared. Payment webhooks are split by currency. GBP events use the Payment* prefix, EUR events use Mccy*.
PaymentTransactionSettled
PaymentTransactionRejected
PaymentStatusChanged
PaymentValidationFailed
PaymentAssesmentFailed
PaymentInboundTransaction
PaymentInboundTransactionHeld
PaymentOutboundTransactionHeld
PaymentInboundInternalTransaction
PaymentInternalTransferSettled
MccyOutboundPaymentComplete
MccyOutboundPaymentFailed
MccyInboundPaymentComplete
MccyInboundInternalTransaction
MccyPaymentStatusChanged
MccyInternalTransferSettled
Request body differences
The GBP and EUR payment requests are similar but not identical. Watch for these differences when building a currency-agnostic payment layer:
GBP uses reference. EUR uses clientReference. If you build an abstraction layer, map this field per currency.
GBP accepts IBAN or account number + sort code. EUR requires IBAN (or SWIFT as fallback). Your payment form should adapt based on currency selection.
GBP FPS runs 24/7. SEPA CT is weekdays 06:00-14:25 UK time only. Build scheduling logic or clear user-facing messaging for EUR payments submitted outside this window.
GBP webhooks use Payment* prefix. EUR uses Mccy*. Your webhook handler needs separate routes or a prefix check to determine the currency context.
Architecture pattern: currency-agnostic service
If you support both currencies, consider abstracting the differences behind a unified interface:
PAYMENT_CONFIG = {
"GBP": {
"account_path": "/api/v1/sterling-account",
"payment_path": "/api/v1/payments",
"client_ref_field": "reference",
"settled_webhook": "PaymentTransactionSettled",
"failed_webhook": "PaymentTransactionRejected",
},
"EUR": {
"account_path": "/api/v1/euro/vibans",
"payment_path": "/api/v1/euro/payments/sepa-ct",
"client_ref_field": "clientReference",
"settled_webhook": "MccyOutboundPaymentComplete",
"failed_webhook": "MccyOutboundPaymentFailed",
},
}
def send_payment(currency, client_ref, debtor_iban, amount, reference, creditor):
config = PAYMENT_CONFIG[currency]
body = {
config["client_ref_field"]: client_ref,
"endToEndIdentifier": generate_unique_id(),
"debtor": {
"iban": debtor_iban,
"paymentAmount": {"currencyCode": currency, "amount": amount},
"paymentReference": reference,
},
"creditor": creditor,
}
path = config["payment_path"]
headers = sign_request("POST", path, body)
return requests.post(f"{BASE_URL}{path}", headers=headers, json=body)
def handle_webhook(event_type, payload):
for currency, config in PAYMENT_CONFIG.items():
if event_type == config["settled_webhook"]:
return mark_payment_settled(payload, currency)
if event_type == config["failed_webhook"]:
return mark_payment_failed(payload, currency)Next steps
GBP account management
Full guide to Sterling virtual IBAN lifecycle.
→EUR accountsEUR virtual IBAN management
Full guide to EUR multi-currency account lifecycle.
→WebhooksWebhook setup and events
All 26 events, handler examples, and gotchas.
→Ready to go multi-currency?
Start by provisioning accounts in the sandbox, then send test payments on both rails.
Create GBP accountCreate EUR accountUpdated about 2 hours ago
