Managed Custody Mode
Core banking API reference for partners integrating via Managed Custody Mode.
This document is the Core Banking OpenAPI reference for Partners integrating with UR in Managed Custody Mode. Section 3 covers onboarding for Partners whose users complete KYC in the Partner's Sumsub flow and share that verification with UR through Sumsub reuse. Fund-moving APIs still require the user's UR account to be
Liveand the mapped Partner user to exist.For the conceptual definition of Managed Custody Mode and how it compares to External Wallet Access Mode, see Integration Guide.
1. Mode context
1.1 Where this API sits
Managed Custody Mode is one of UR's two Account Modes. In this mode:
The user's UR account (URID + tokenized fiat balances) lives inside a UR-managed embedded wallet. For compliance, this wallet holds tokenized fiat only; it never custodies the user's crypto, which always sits in an external (non-UR) wallet.
The Partner backend accesses that account entirely through REST APIs, signed with the Partner's signer key.
Your backend submits routine banking actions (FX, internal transfers, Pay-in, Payout, On-ramp (coming soon), Off-ramp, Card). UR validates each request, runs compliance, risk, and limit checks, and executes on-chain settlement using UR's wallet infrastructure.
The user is not prompted to sign on-chain transactions for routine banking actions.
The Partner owns the entire UX surface; UR is the regulated financial infrastructure underneath.
1.2 Operation layer map
Every Core Banking endpoint maps to one of UR's seven core operations. This table is the canonical anchor for the rest of this document.
Card
User → merchant via Mastercard
§11 (Card-Mode-specific; see API Reference: Card Mode: Crypto Backed)
Real-time authorization, async settlement
Read-only endpoints (Profile §5, Balance §6, Transactions §12) sit beside these operations.
2. API foundation
2.1 Base URLs
Production
https://openapi.ur.app
Preview
https://openapi-preview.ur.app
Testnet
https://uropenapi-qa.ur-inc.xyz
Confirm the exact base URL set with UR before production rollout.
2.2 Authentication: Partner Auth (EIP-191)
All authenticated Partner → UR requests use Partner Auth with EIP-191 signatures.
Every authenticated request must include:
X-Api-Signature
Yes
0x-prefixed 65-byte hex signature over the Partner Auth message, produced with the Partner signer's private key.
X-Api-Deadline
Yes
Unix seconds. UR rejects the request if now > deadline. Recommended validity window ≤ 5 min.
X-Api-PublicKey
Yes
The Partner signer address (0x-prefixed). Must be registered with UR.
Canonical payload:
GETrequests sign the raw query string exactly as sent, without the leading?.Non-
GETrequests sign the raw request body exactly as sent.If the request has no query string or body, use an empty string.
The canonical payload is part of the signed message. {canonicalPayload} is not literal text. Replace it with the exact request body or query string that your backend sends.
Build the Partner Auth message from the canonical payload, user identity suffix, and deadline:
If an identity header is not sent, use an empty value in its slot. Do not add a separator before urId:. Add one ASCII space before X-Api-Deadline.
For example, a POST body of {"amount":"100"} with X-Ur-Id: 7123456789, no X-External-User-Id, and X-Api-Deadline: 1772002211 signs:
For a GET /api/fma/v1/kyc/form-a-info?sessionId=abc123 request with the same headers, sign:
For the EIP-191 recovery algorithm, see Signature and Verification.
2.3 User identity headers
Every user-scoped Partner → UR endpoint must identify the user with at least one of the following headers.
X-Ur-Id
The user's URID (numeric token ID of the URID NFT).
X-External-User-Id
The Partner's own user ID, mapped to a URID during onboarding.
Rules:
Send at least one of
X-Ur-IdorX-External-User-Id. Sending neither is invalid.Sending both is allowed. When both are present, UR resolves the user by
X-Ur-Idfirst.User identity must not be duplicated in query parameters or request bodies for user-scoped APIs.
Example:
or:
2.4 Header block references
To avoid repeating long header tables, endpoint sections refer to these named blocks.
Partner-Scoped Partner Auth Headers: Partner Auth headers (§2.2) only, no user identity. Used for partner-level endpoints not tied to a single user.
Public Metadata Headers: No auth required. Used for fully public reference endpoints (banks, payment purposes, etc.). UR reserves the right to change this access policy.
2.5 Standard response envelope
Core Banking APIs use the standard UR OpenAPI response envelope:
code = 0→ success; non-zero → business error (see endpoint-specific tables and the global error code reference).message→ human-readable explanation, may be empty on success.data→ endpoint-specific payload.
2.6 Idempotency
Endpoints that move funds (Off-ramp submission, FX, internal transfer, Payout, On-ramp, Onramp retry) accept a Partner-supplied idempotency key:
The field name is
reqId.Keep
reqIdstable across retries of the same logical operation. If a response is lost or times out, query transaction history byreqIdbefore retrying. Do not generate a newreqIdfor the same logical operation.Webhook delivery is at-least-once; use
data.txHashor the transactionidas the idempotency key on the Partner side.
2.7 Preconditions for fund-moving APIs
Before calling Off-ramp, On-ramp, FX, internal transfer, Payout, or Card APIs, the Partner must ensure:
The UR account status is Live.
The mapped Partner user and UR account exist and are not frozen.
Any integration-specific approval required by UR has been enabled for your production setup.
3. Onboarding
Authoritative reference: the share-token reuse flow is specified in full, with exact endpoints, request/response fields, and error codes, in API reference: Shared-token KYC reuse. Where this section and that reference differ, the shared-token reference wins.
Use this section when your platform verifies the user through your own Sumsub tenant. During onboarding, your platform mints a single-use Sumsub share token (scoped to UR's clientId) and hands it to UR. UR imports the applicant via Sumsub Copy Applicant and runs data-level validation over the copied snapshot (it does not re-run Sumsub checks against a UR level), creates a URID, provisions the UR-managed wallet, renders and signs Form A, and activates the user's UR Account.
Two onboarding paths are supported:
Sumsub reuse (share token), this page. Your platform completes KYC in your own Sumsub tenant and shares the approved applicant with UR through Sumsub reuse. Use this when you already operate a Sumsub tenant.
Sumsub SDK in your app. Your backend requests a UR-issued Sumsub access token and the Sumsub SDK in your app runs KYC against UR's Sumsub tenant. See API reference: Managed Custody SDK KYC. Use this when you do not run a Sumsub tenant.
Both paths produce the same end state and use the same post-onboarding banking APIs documented in this page.
3.1 Prerequisites
Before you start onboarding users, make sure the following setup is complete:
Your Partner Auth signing key is registered with UR. See API signing key for how to create it in the API sandbox or register your own address.
Your platform and UR are configured as Donor / Recipient Partners in Sumsub.
Your KYC flow presented the required data-sharing declaration and the user agreed, before identity verification. This declaration is mandatory and is the user-facing basis for the Sumsub reuse on this page; see the required KYC disclosure.
Your backend can generate a single-use Sumsub share token scoped to UR's Sumsub
clientIdafter the applicant is approved.Your backend can hand the share token to UR via
POST /api/fma/v1/kyc/reuse-share-token.Your backend can persist
externalUserId,sessionId,urId, andevmAddressfor each user.Your backend can receive onboarding webhooks from UR:
fma.kyc.reuse_check.result(the async handoff verdict) andfma.account.result(final activation). After a user is live,fma.additional_kyc.requiredandfma.additional_kyc.completedcover ops-initiated retries.
Store the returned identifiers before you show the user that onboarding has started. Your externalUserId is your stable user ID. UR maps that value to the returned urId.
3.2 Identity headers during onboarding
Onboarding uses the same Partner Auth rules as the rest of this page. The identity header changes after UR creates the user's UR Account.
Pre-account
POST /api/fma/v1/create-account
X-External-User-Id
Do not send X-Ur-Id before UR returns the user's urId.
Post-account
/api/fma/v1/kyc/* and /api/fma/v1/account-status
X-Ur-Id
Use the urId returned by /create-account. Sending X-External-User-Id as well is allowed.
For GET /api/fma/v1/kyc/form-a-info and GET /api/fma/v1/account-status, sign the raw query string exactly as sent. For POST endpoints, sign the raw request body exactly as sent. Append the same identity suffix and deadline described in §2.2.
3.3 Onboarding flow
The onboarding flow starts after the user has completed KYC in your Sumsub workflow and your backend has minted a single-use share token for UR. You create the UR Account, then hand the share token to UR: UR imports the applicant via Copy Applicant and returns a synchronous verdict (passed / incomplete / terminal). /kyc/check is a read of that verdict; it does not itself trigger the import.
Create the UR Account
Call POST /api/fma/v1/create-account with your X-External-User-Id and the user's email. For share-token (push-mode) partners, do not send applicantId; it is rejected. UR creates or reuses the user's URID, provisions the UR-managed wallet, and returns sessionId, urId, and evmAddress.
Hand off the share token
Call POST /api/fma/v1/kyc/reuse-share-token with the single-use shareToken. UR imports the applicant via Copy Applicant and returns a synchronous verdict: passed, incomplete (with missingFields to remediate and hand off again), or terminal (non-remediable eligibility rejection).
3.4 Create account
Create or reuse the user's UR Account and onboarding session.
Method
POST
Path
/api/fma/v1/create-account
Headers
Partner Auth headers + X-External-User-Id
Request body:
Request fields:
email
Yes
The user's email address.
For share-token (push-mode) partners, do not send applicantId; the endpoint rejects a non-empty applicantId (20003). applicantId is only for pull-mode partners, which is a different integration. The applicant is conveyed later via the share token in /kyc/reuse-share-token, not here.
Response example:
Rules:
Repeated calls with the same
X-External-User-Idreturn the existing onboarding session withidempotentReplay=true.Persist
sessionId,urId, andevmAddressbefore continuing.Use
X-Ur-Idon subsequent onboarding calls.
3.5 Check KYC completeness
Re-read the last handoff verdict (from /kyc/reuse-share-token) and check whether the session can proceed to Form A. This is a pure read: it does not import the applicant or re-run any check; the import happens in /kyc/reuse-share-token.
Method
POST
Path
/api/fma/v1/kyc/check
Headers
User-Scoped Partner Auth Headers
Request body:
Complete response:
Incomplete response:
The handoff verdict is available synchronously from /kyc/reuse-share-token and via the fma.kyc.reuse_check.result webhook, so you normally do not need to poll. If you do poll, stop after /kyc/submit succeeds or when the session reaches a terminal state.
Error handling:
20004
The KYC snapshot is incomplete.
Read missingFields, have the user complete the missing data, then mint a fresh share token and hand off again.
40001
Sumsub is temporarily unavailable.
Back off and retry.
3.6 Get Form A
Fetch the exact Form A text that the user must review.
Method
GET
Path
/api/fma/v1/kyc/form-a-info?sessionId={sessionId}
Headers
User-Scoped Partner Auth Headers
Request body: none.
Response example:
Rules:
Display
data.textto the user exactly as returned.Pass
data.textHashunchanged to/kyc/sign-form.textHashis the0x-prefixed keccak256 hash of the UTF-8 bytes ofdata.text.If this endpoint returns
20007, the KYC snapshot is not ready. Return to/kyc/check.
3.7 Sign Form A
Ask UR to sign the rendered Form A text with the user's UR-managed custodial wallet.
Method
POST
Path
/api/fma/v1/kyc/sign-form
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
Rules:
The request does not include a user signature. UR produces the Form A signature using the user's UR-managed wallet.
textHashmust match the latest Form A text rendered by UR.If user data changes before
/kyc/submit, call/kyc/form-a-infoagain and re-sign the latesttextHash.
3.8 Submit onboarding
Submit the completed onboarding session for asynchronous account activation.
Method
POST
Path
/api/fma/v1/kyc/submit
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
The registrationId can be empty in the synchronous response. UR fills downstream activation details asynchronously. Use fma.account.result or /api/fma/v1/account-status to confirm the final state.
Preconditions:
/kyc/checkhas returnedcomplete=true.Form A has been signed through
/kyc/sign-form.The session is in
Registerstate.
3.9 Get account status
Use account status as a polling fallback after /kyc/submit, or as an explicit confirmation before enabling fund-moving features.
Method
GET
Path
/api/fma/v1/account-status
Headers
User-Scoped Partner Auth Headers
Response example:
Poll this endpoint at a 1-minute cadence after /kyc/submit if you do not receive fma.account.result. Stop polling when data.statusStr is Live, Blocked, or Closed.
3.10 Onboarding states
Use the following state values for your local onboarding cache:
PartnerDataIngestion
UR created the onboarding session and is waiting for a complete KYC snapshot.
Call /kyc/check after the Sumsub applicant is complete.
IdentityVerification
UR is still validating identity evidence.
Continue polling /kyc/check, or ask the user to complete missing KYC steps.
SignFormA
The KYC snapshot is ready for Form A.
Call /kyc/form-a-info, display the text, then call /kyc/sign-form.
Register
Form A is signed and the session is ready to submit.
Call /kyc/submit.
Submitting
UR is activating the account with the downstream banking partner.
Wait for fma.account.result or poll /api/fma/v1/account-status.
Completed
The UR Account is activated.
Enable fund-moving features only after account-status returns Live.
Failed
UR rejected or expired the onboarding session.
Show the failure state. UR operations may later issue a retry, which arrives as fma.additional_kyc.required; see Retry KYC.
A retry session reports states from this same set, but only the subset its reduced step list covers, so it can start at SignFormA with no earlier state ever observed. Treat an unexpected state as "a step remains" rather than an error.
3.11 KYC webhooks
UR sends these webhooks to the URL registered for your integration. The first two cover onboarding; the retry events fire later, once the user is already live. Verify every webhook with the same EIP-191 recovery rules in §13.2.
Activation or rejection:
When data.status is activated, confirm Live with /api/fma/v1/account-status before enabling fund-moving features. When data.status is rejected, the payload includes rejectCode and rejectReason.
Retry required: UR ops can ask an already-onboarded user to redo part or all of their KYC. That arrives as fma.additional_kyc.required:
The new session is not in the payload. Call POST /api/fma/v1/kyc/session/create to claim it, then run only the steps taskType names. Do not call /create-account for a retry. A second event, fma.additional_kyc.completed, fires when the retry session finishes. See Retry KYC for the full walkthrough and Webhooks for both payload contracts.
4. Core banking integration principles
The following rules apply across all fund-moving endpoints in this reference.
User balances are queried with
GET /api/fma/v1/balance(§6.1).Off-ramp converts crypto (held on a supported source chain) into the user's tokenized fiat balance. For the current set of supported source chains, source tokens, and target fiat currencies, see Supported Chains & Tokens.
On-ramp converts the user's tokenized fiat balance into crypto on a supported destination chain. New On-ramp submissions must be blocked while a pending retry exists. For the current set of supported destination chains and tokens, see cryptos with
aggregatorSupportedvalue in the response of Supported Chains & Tokens.Card authorization behavior depends on the Partner's Card Mode. See API Reference: Card Mode: Crypto Backed for the Crypto Backed integration surface; Card Mode: Fiat Only has no Partner-side authorization surface.
All async settlement results are delivered via the transaction webhook (§13). The webhook is the authoritative source of truth; API responses to fund-moving calls return only a
txHash(the operation has been submitted on-chain, not yet settled).
5. Profile
5.1 Get BR profile
Fetch the user's banking profile, including IBAN, fiat limits, contacts, deposit bank details, and card eligibility.
Method
GET
Path
/api/fma/v1/br
Headers
User-Scoped Partner Auth Headers
Request body: none. Query parameters: none.
Response example:
Notes:
limitsare denominated in CHF and use a rolling 30-day window.FX, card spending, on-ramp, and payout share the same fiat limit bucket; each of these is checked against
limits.availableand fails if it exceeds it.A single outgoing transaction must not exceed
limits.available.ibanis the user's default personal Swiss IBAN. It receives EUR and CHF deposits; it does not receive USD.depositBankis keyed by currency. For each inbound transfer, read the entry whose key matches the deposit currency, and show that account to the user.A USD IBAN is separate from the EUR/CHF IBAN. UR provisions the EUR/CHF IBAN automatically when the user reaches
Live, with no prerequisite pay-in. UR provisions the USD IBAN only on request: callPOST /v1/apply-usd-payinwhen the user wants to receive USD. The call is synchronous, and UR creates the USD IBAN immediately if the user isLive. The USD deposit account then appears under theUSDkey ofdepositBank. Match the account to the currency the user will send; never reuse the EUR/CHF IBAN for a USD transfer. USD deposits from a non-same-name sender are held for review; see Deposits.
6. Balance
6.1 Get user balance
Fetch the user's fiat balances held inside the user's UR account. In Managed Custody Mode the UR-managed account holds tokenized fiat only.
Method
GET
Path
/api/fma/v1/balance
Headers
User-Scoped Partner Auth Headers
Response example:
fiatItemsenumerates the user's tokenized fiat balances on Mantle (EUR24, CHF24, USD24, etc.).The endpoint returns fiat only. The UR-managed account never custodies crypto, so there is no crypto balance to report; the user's crypto sits in an external (non-UR) wallet.
7. Off-ramp
Off-ramp converts crypto into fiat in the user's UR account.
Currently supported:
Source chains and tokens: see Supported Chains & Tokens
Target fiat currencies: USD, EUR, CHF, SGD, JPY, HKD
Amount limits. Read the minimum and maximum Off-ramp amount for each source token from the chain config fields minTopUpAmount and maxTopUpAmount; see Supported Chains & Tokens. Read them at request time and do not hardcode them, because UR converts a USD anchor at the live rate for each token. A USDC Off-ramp must be at least 5 USDC. Each Off-ramp also counts against the user's rolling 30-day fiat limit.
Flow
The UR API step is quote retrieval. After the Partner receives the quote, the holder of the external crypto wallet (the user, or the Partner when the wallet is partner-side) signs and submits the Off-ramp contract call. See §7.2.
7.1 Get off-ramp quote
Method
POST
Path
/api/fma/v1/quote/deposit
Headers
User-Scoped Partner Auth Headers
Request body:
chainId&fromToken: see Supported Chains & TokenstoCurrency: target fiat currency symbol.amount: human-readable decimal string; UR converts it to token smallest units using the source token decimals.
Response example:
Tempo chain only: amountReceived is the actual USDC amount received on Arbitrum (in smallest unit). This field is only returned for Tempo chain deposits.
Contract execution notes:
Pass
best.to,best.swapCalldata, andbest.minUsdcAmountto the UR Off-ramp contract exactly as returned.The signing external Crypto Wallet (see §7.2) must have approved the Off-ramp contract to spend
fromTokenfor at leastamount.The transaction must be submitted before
best.deadline; otherwise it can revert.networkFeeandcrossChainFeeare denominated in the source chain's native token and paid by the user from the source chain wallet (in addition toamount).Final settlement is reported asynchronously through the transaction webhook with
data.type = "CRYPTO_DEPOSIT".
7.2 Initiate off-ramp
In Managed Custody Mode, the Fiat Wallet is always UR-managed and the Crypto Wallet is always an external (non-UR) wallet: the partner's account or the user's own. For compliance, the UR-managed account never holds crypto: the source crypto is paid in from that external wallet, and UR credits the resulting fiat to the user's UR Account. The Off-ramp contract uses the _targetAccount parameter to identify which UR Account receives the resulting fiat.
Contract: depositTokenViaAggregatorToAccount on the Off-ramp contract. Contract addresses per chain: see Deposit (off-ramp).
Contract Parameters:
_inputToken
address
Yes
Source token address (Use 0x00...00 for native tokens).
"0xA0b8...B48" (USDC)
_outputToken
address
Yes
Target fiat token address (Fiat type after deposit).
"0x1234...5678"
_amount
uint256
Yes
Deposit amount (in smallest unit, e.g., Wei).
For USDC: "10000000" = 10.000000
_aggregator
address
Yes
Exchange contract address; use best.to from §7.1.
_swapCalldata
bytes
Yes
Use best.swapCalldata from §7.1.
"0x" for USDC direct deposit.
_minUsdcAmount
uint256
Yes
Use best.minUsdcAmount from §7.1.
_feeAmountViaUsdc
uint256
Yes
Put "0" when user calls the contract directly.
_targetAccount
address
Yes
Use data.targetAccount from §7.1. This is the user's UR Account address that receives the resulting fiat.
For Tempo chain, use depositWithFeeTo on the Tempo Off-ramp contract. Contract addresses per chain: see Deposit (off-ramp).
Contract Parameters:
recipient
address
Yes
The user's UR Account address that receives the resulting fiat. Used when the signing wallet is not the user's UR Account itself.
inputToken
address
Yes
Source stablecoin address.
inputAmount
uint256
Yes
Deposit amount (in smallest unit, e.g., Wei).
outputToken
address
Yes
Target fiat token address.
minAmountReceived
uint256
Yes
Minimum amount to receive on Arbitrum. Calculate from the quote API: amountReceived adjusted by slippageBps.
refundAddress
address
Yes
Address to receive refund of excess cross-chain fee. Usually the user's wallet address.
maxFeeUsdcAmount
uint256
Yes
First-hop (Tempo → Arbitrum) cross-chain fee budget (denominated in USDC, in smallest unit). Calculate from the quote API: crossChainFee adjusted by slippageBps.
feeAmountViaUsdc
uint256
Yes
Put "0" when user calls the contract directly.
If you require Partner-side API submission for Off-ramp instead of on-chain user signing, please contact the UR team.
8. FX and internal transfers
This section covers two on-chain fiat operations. FX converts one tokenized fiat balance into another inside the user's UR Account. Internal transfers send one tokenized fiat balance from the user's UR Account to a different UR Account.
Amount limits. Read the minimum and maximum FX amount for each token from the chain config fields minFxAmount and maxFxAmount; see Supported Chains & Tokens. Read them at request time and do not hardcode them. UR converts the minimum from a USD anchor at the live rate. The maximum reflects the user's remaining rolling 30-day allowance, so it changes over time.
8.1 FX quote
Method
POST
Path
/api/fma/v1/quote/fx
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
8.2 Execute FX
Method
POST
Path
/api/fma/v1/fx-exchange
Headers
User-Scoped Partner Auth Headers
Request body:
amountOutMinimum is optional. If omitted, UR applies a default 0.5% slippage buffer based on the submitted amount.
Response example:
Final result is reported through the transaction webhook with data.type = "FRX". See §12.1 for the full set of status values.
8.3 Initiate an internal transfer
Send tokenized fiat from the user's UR Account to another UR Account. The User-Scoped Partner Auth Headers identify the sender. Send the recipient's URID in toAccountId; do not put the sender's URID in the request body.
Method
POST
Path
/api/fma/v1/internal-transfer
Headers
User-Scoped Partner Auth Headers
Request body:
Request fields:
reqId
string
Yes
Partner-supplied idempotency key. Keep this value stable for the same logical transfer.
amount
string
Yes
Positive human-readable decimal amount. Do not send the amount in token smallest units.
currency
string
Yes
Tokenized fiat currency configured for the endpoint: EUR, CHF, or USD.
toAccountId
string
Yes
Recipient URID as a non-negative decimal integer string. It must differ from the sender's URID.
Response example:
The response confirms submission, not settlement. Confirm the final result through the transaction webhook or query POST /api/fma/v1/transactions by txHash or reqId. The transaction type is INTERNAL_TOKEN_TRANSFER; a successful transfer reaches CONFIRMED.
UR returns a non-zero business code synchronously when the user identity, recipient URID, currency, amount, or reqId is invalid. An insufficient balance or an on-chain execution failure can surface asynchronously as FAILED or REJECTED. Do not credit the recipient based only on the submission response.
9. Bank payout
Bank Payout sends tokenized fiat from the user's UR account to an external bank account via SEPA / SWIFT.
Flow
9.1 Get payout fees
Method
GET
Path
/api/v1/banks/payout/fees
Headers
Public Metadata Headers
Public metadata, not scoped to a single user. Returns the standard envelope:
9.2 Choose recipient
The Partner can use either a recent contact returned by GET /api/fma/v1/br, or create / verify a new contact.
Recent contact path:
Read
data.contacts[currency]from the BR Profile response (§5.1).Use
contact.idascontactId.If the user provides a new reference, call
POST /api/fma/v1/verify-reference(§9.3) to get a freshrefId+purposeId.
New contact path:
Identify the recipient bank by IBAN (
GET /api/v1/banks/iban/{iban}) or by selecting from the non-IBAN bank list (GET /api/v1/banks).Collect required creditor name, address, country, city, payment purpose, and reference.
Call
POST /api/fma/v1/verify-contact(§9.4).
Public metadata APIs (/api/v1/banks, /api/v1/banks/iban/{iban}, /api/v1/country-cities, /api/v1/payment-purposes) do not require user identity headers unless UR changes their access policy.
9.3 Verify reference
Method
POST
Path
/api/fma/v1/verify-reference
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
9.4 Verify contact
Method
POST
Path
/api/fma/v1/verify-contact
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
creditorInfo.name, creditorInfo.street, creditorInfo.city, and creditorInfo.country must use Latin characters.
9.5 Submit payout
Method
POST
Path
/api/fma/v1/submit-payout
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
Constraints:
metadataname, address, and reference values must use Latin characters.purposeIdandrefIdmust be provided together, or both omitted.Minimum amount is currency-specific and comes from
minimalPayoutAmount(§9.1).Network fees and payout fees are deducted from
amount; they are not charged separately.Payout is subject to the user's rolling 30-day CHF-denominated fiat limits.
Final result is reported through the transaction webhook with
data.type = "FIAT_WITHDRAW".
10. On-ramp
Available soon. On-ramp (fiat-to-crypto) is not yet available for integration and will be enabled in a future release. The reference below is provided for preview only.
On-ramp converts the user's tokenized fiat balance into crypto, delivered to an external wallet on a target chain. The UR-managed account holds fiat only, so On-ramp crypto never lands in the UR account.
Currently supported:
Destination chains and tokens: see cryptos with
aggregatorSupportedvalue in the response of Supported Chains & Tokens.Source fiat currencies: USD, EUR, CHF, SGD, JPY, HKD
Flow
10.1 On-ramp login initialization
When the user enters the On-ramp flow, the Partner should run these checks in order:
GET /api/fma/v1/onramp-limitGET /api/fma/v1/onramp/pending-retry, only if the limit response allows the flow.
If a pending retry exists, the Partner must force the user to Retry or Cancel before starting a new On-ramp.
10.2 Get on-ramp limit
Method
GET
Path
/api/fma/v1/onramp-limit
Headers
User-Scoped Partner Auth Headers
Response example:
Block the flow if regionLocked, usdcDepegged, or livenessLocked is true, or if the requested amount falls outside the minAmounts[currency] to maxAmounts[currency] range.
maxAmounts and minAmounts are keyed by fiat currency. Read both at request time; do not hardcode them. UR derives them from a single USD anchor (about 5 USD minimum; the maximum depends on the user's liveness state) and converts each to fiat at the live rate, so the values differ across currencies.
10.3 Check pending retry
Method
GET
Path
/api/fma/v1/onramp/pending-retry
Headers
User-Scoped Partner Auth Headers
Pending retry represents an On-ramp whose bridge succeeded but whose swap leg failed; the user must resolve it before starting a new On-ramp.
No pending item:
When there is no pending retry, data.originalTxHash is empty.
Pending item:
10.4 Get on-ramp quote
Method
POST
Path
/api/fma/v1/quote/onramp
Headers
User-Scoped Partner Auth Headers
Request body for the main On-ramp flow:
Notes:
sceneisonrampfor the main flow andswap_retryfor retry (§10.8).srcChainIdis the chain where the user's tokenized fiat is held (UR's home chain).fromCurrencyis required forscene = "onramp".fromTokenis used only forscene = "swap_retry".dstChainIdandtoTokenmust match a crypto type withaggregatorSupportedvalue in the response of Supported Chains & Tokens.networkFeereturned in the quote response is the destination-chain gas + cross-chain fee, deducted from the user's input fiat.If the response has
needLiveness = true, the Partner must complete liveness before submitting On-ramp.
10.5 Liveness token
Method
GET
Path
/api/fma/v1/onramp-liveness-token
Headers
User-Scoped Partner Auth Headers
Only call this endpoint when a quote returns needLiveness = true.
Response example:
10.6 Liveness result
Method
GET
Path
/api/fma/v1/onramp-liveness-result
Headers
User-Scoped Partner Auth Headers
Response example:
After liveness_result = "pass", the Partner should request a new quote and submit using the new quoteId.
10.7 Submit on-ramp
Method
POST
Path
/api/fma/v1/onramp
Headers
User-Scoped Partner Auth Headers
Request body:
Response example:
Submit constraints:
amountInis a human-readable decimal string and must match the amount used for the cached quote.quoteIdmust match UR's cached quote (and not be expired).A quote requiring liveness cannot be submitted until liveness passes.
New On-ramp must be blocked while a pending retry exists (§10.3).
withdrawAddressis the external wallet that receives the crypto. It is required for every On-ramp, same-chain or cross-chain, because the UR-managed account holds fiat only and never receives crypto.UR delivers the destination-chain crypto to
withdrawAddress; UR never sends On-ramp crypto to the user's UR account.Final result is reported through the transaction webhook.
10.8 Retry on-ramp swap
On-ramp is a two-leg flow (bridge + swap). When the bridge succeeds but the swap fails, the user's funds are stuck as the bridge intermediate token on the destination chain. Retry redoes the swap leg only, so the payload drops fiat/source-chain inputs and instead carries originalTxHash, the post-bridge intermediate-token amount, and a fresh swap quote.
Method
POST
Path
/api/fma/v1/onramp-swap
Headers
User-Scoped Partner Auth Headers
Only call this endpoint when GET /api/fma/v1/onramp/pending-retry returns a pending item.
Retry flow:
Read pending retry from
GET /api/fma/v1/onramp/pending-retry.Request a fresh retry quote:
scene = "swap_retry"srcChainId = pendingRetry.chainIddstChainId = pendingRetry.chainIdfromToken = pendingRetry.fromTokentoToken = pendingRetry.toTokenamount = pendingRetry.amount(human-readable)
Submit
/api/fma/v1/onramp-swap:usdcAmount = pendingRetry.amountRaw(USDC minimal unit)tokenOut = pendingRetry.toTokenminAmountOut = quote.best.minAmountOutaggregator = quote.best.to(notquote.best.aggregator)swapCalldata = quote.best.swapCalldata
Request body:
10.9 Cancel on-ramp retry
Method
POST
Path
/api/fma/v1/onramp/retry/cancel
Headers
User-Scoped Partner Auth Headers
Request body:
A successful response clears the pending retry record and allows the Partner to re-enable the normal On-ramp entry point.
11. Card
The user's debit card is issued and processed by UR through Mastercard. This section covers only the card-management endpoints common to all Card Modes: card creation, card info retrieval, default-currency selection, and post-settlement history.
Card authorization, prefund, and card-related webhooks are Card-Mode-specific. Card Mode: Fiat Only has no Partner-side authorization surface; UR handles authorization on-chain against the user's tokenized fiat balance. Card Mode: Crypto Backed has its own integration surface (synchronous authorization callback, Prefund Account, Prefund Balance Alert webhook) documented in API Reference: Card Mode: Crypto Backed.
11.1 Create card
Create a virtual card for an eligible Live user.
Method
POST
Path
/api/fma/v1/open-card
Headers
User-Scoped Partner Auth Headers
Request body: {}
Preconditions:
GET /api/fma/v1/brreturnsisCardEligible = true.The user has no existing card if UR only allows one card per user.
The user balance satisfies
cardActivation.amountandcardActivation.currency.
Response example:
11.2 Get card info
Fetch card metadata and a short-lived cardToken for secure card display.
Method
GET
Path
/api/fma/v1/card
Headers
User-Scoped Partner Auth Headers
Response example:
Display card details:
The card info API does not expose real PAN, CVV, or expiry in JSON. Use cardToken only to render those sensitive fields through UR's card display script. The cardToken is short-lived and expires after 5 minutes. When it expires, call GET /api/fma/v1/card again to get a fresh token.
Card identifiers:
cardToken
Short-lived token for card detail display only. Do not store or log it.
externalId
Stable card management ID. Use it for APIs such as Set Default Card Currency.
activeTokens[].id
Device wallet token ID, such as Apple Pay. Do not use it for card detail display or currency settings.
Load the script from UR:
Add DOM placeholders where the script should render sensitive fields:
Initialize the display after the user chooses to reveal card details:
Common mistakes:
Do not store or log
cardToken.Do not use
activeTokens[].idunless calling a device-token management API.Load the script only on the card details view or secure webview, not globally across your app.
Render card details only after explicit user action, such as selecting "Show card details".
11.3 Set default card currency
Set the user's default card transaction currency.
Method
POST
Path
/api/fma/v1/card-currency
Headers
User-Scoped Partner Auth Headers
Request body:
Request fields:
currency
string
Yes
Target default transaction currency, such as USD, EUR, or CHF.
Response example:
Notes:
The server automatically resolves the card's
externalIdfrom the authenticated user; the Partner does not need to pass it.The next
GET /api/fma/v1/cardresponse should show the updatedcurrency.This setting affects UR's default refund currency display and debit preference.
Per-transaction overrides at swipe time are governed by the Card Mode; see API Reference: Card Mode: Crypto Backed.
11.4 Update card status
Block or unblock the user's card.
Method
POST
Path
/api/fma/v1/update-card-status
Headers
User-Scoped Partner Auth Headers
Request body:
Request fields:
statusChange
string
Yes
The status transition to apply. Accepted values: block, unblock.
Response example:
Notes:
The server automatically resolves the card's
externalIdfrom the authenticated user.A blocked card will decline all authorization attempts until unblocked.
11.5 Card authorization
Card authorization behavior is Card-Mode-specific and is not covered in this document. See API Reference: Card Mode: Crypto Backed for the Crypto Backed integration surface. Card Mode: Fiat Only has no Partner-side authorization surface.
11.6 Card settlement notes
Card settlement result records are exposed through transaction history (§12) with data.type = "CRD". Additional card adjustment event contracts (chargebacks, fee adjustments) must be agreed separately with UR.
12. Transactions
Use transaction history for reconciliation. The same endpoint also supports exact lookup by transaction hash or transaction ID.
12.1 Fetch transaction history
Method
POST
Path
/api/fma/v1/transactions
Headers
User-Scoped Partner Auth Headers
Simple first-page request:
Filtered request:
Response example:
Pagination is driven by the response flags and cursors. Only send cursor fields returned by the API, and only when the corresponding flag is true.
To fetch the next page, use nextCursor when hasNextPage is true:
To fetch the previous page, use prevCursor when hasPrevPage is true:
If hasPrevPage is false, do not use prevCursor; a zero cursor means there is no previous page.
Request fields:
pageSize
integer
Page size. Defaults to 50; maximum is 100.
type
string
Single transaction type filter. Do not send together with txTypes.
txTypes
string[]
Multiple transaction type filter. Do not send together with type.
currencies
string[]
Currency filter. Values are normalized to lowercase by UR.
direction
string
Direction filter: IN, OUT, or ALL.
status
string
Transaction status filter.
chainId
string
Chain ID filter, for example eip155:5000.
tokenSymbol
string
Token symbol filter, for example USDC.
minAmount
string
Minimum amount filter.
maxAmount
string
Maximum amount filter.
fromTimestamp
integer
Start timestamp filter.
toTimestamp
integer
End timestamp filter.
cursorTimestamp
integer
Next-page cursor timestamp from data.nextCursor.timestamp.
cursorId
integer
Next-page cursor ID from data.nextCursor.id.
prevCursorTimestamp
integer
Previous-page cursor timestamp from data.prevCursor.timestamp.
prevCursorId
integer
Previous-page cursor ID from data.prevCursor.id.
id
integer
Exact lookup by UR internal transaction ID. Mutually exclusive with txHash and reqId.
txHash
string
Exact lookup by transaction hash. Mutually exclusive with id and reqId.
reqId
string
Exact lookup by the idempotency key (reqId) used when the transaction was created. Mutually exclusive with id and txHash.
Partner-relevant transaction types:
type
Meaning
CRYPTO_DEPOSIT
Crypto deposit / tokenized fiat top-up.
INTERNAL_TOKEN_TRANSFER
Internal token transfer between UR accounts.
UNKNOWN
Unknown or uncategorized transaction type.
FX_EXCHANGE
FX exchange.
MARQETA_AUTHORIZE
Card authorization / card payment.
FIAT_WITHDRAW
Fiat withdrawal / bank payout.
FIAT_DEPOSIT
Fiat deposit / bank pay-in.
ONRAMP
On-ramp.
Transaction status values:
status
Description
UNKNOWN
Unknown; default value. Should not appear in normal flows.
INIT
Transaction created internally but not yet broadcast to the blockchain node.
PENDING
Transaction is in the mempool (transaction pool), awaiting confirmation.
CONFIRMED
Transaction has been confirmed and settled on-chain.
FAILED
Transaction execution failed on-chain.
PENDING_DROP
Transaction is scheduled to be dropped, for example replaced or cancelled.
DROPPED
Transaction was removed from the mempool without being confirmed.
REJECTED
Transaction was rejected by UR's validation or compliance checks before or during execution.
12.2 Fetch transaction details
Use this endpoint to fetch a single transaction. Send exactly one lookup key: txHash for an on-chain transaction hash, id for UR's internal transaction ID, or reqId for the idempotency key used when the transaction was created.
Method
POST
Path
/api/fma/v1/transactions
Headers
User-Scoped Partner Auth Headers
Lookup by transaction hash:
Lookup by UR internal transaction ID:
Lookup by idempotency key:
Response:
The response uses the same
data.items[]transaction structure as §12.1.Send exactly one of
id,txHash, orreqId; they are mutually exclusive.If no transaction matches the lookup key,
data.items[]is empty.
12.3 detailsJson reference by transaction type
The detailsJson field is a stringified JSON blob whose structure depends on the transaction type. Parse the string before reading nested fields.
Field names follow protobuf camelCase conventions. All fields are optional (omitempty) unless stated otherwise.
CRYPTO_DEPOSIT (crypto top-up)
Crypto deposit to fund a fiat account.
fromChainId
string
Source chain ID (e.g., eip155:1 for Ethereum mainnet)
fromTxHash
string
Transaction hash on the source chain
fromTxLogIndex
string
Log index on the source chain
inputToken
string
Input token symbol (e.g., USDC, USDT)
inputAmount
string
Input token amount
inputTokenAddress
string
Input token contract address
bankTxHash
string
Settlement transaction hash on Mantle chain
externalSender
string
External sender address
externalTarget
string
External target address
externalTargetTokenId
string
Target token ID on UR
partnerId
string
Partner identifier
partnerRefId
string
Partner reference ID
FX_EXCHANGE (forex exchange)
Fiat-to-fiat currency exchange.
inputAmount
string
Amount of input (sold) currency
outputAmount
string
Amount of output (bought) currency
inputCurrency
string
Input currency code (e.g., USD)
outputCurrency
string
Output currency code (e.g., EUR)
partnerId
string
Partner identifier
partnerRefId
string
Partner reference ID
INTERNAL_TOKEN_TRANSFER (P2P transfer)
Peer-to-peer transfer between UR accounts.
fromAddress
string
Sender wallet address
toAddress
string
Recipient wallet address
fromURId
string
Sender UR ID
toURId
string
Recipient UR ID
fromNickName
string
Sender display name
toNickName
string
Recipient display name
Special
fromURIdvalues:9102/9101/9103indicate a bank transfer refund.9110/9113indicate a card spending refund.982indicates UR Rewards.
MARQETA_AUTHORIZE (card payment)
Card authorization / card spending.
authorizationId
string
Card authorization identifier
originAuthorizationId
string
Original authorization ID (for refunds/adjustments)
merchantId
string
Merchant identifier
merchant
string
Merchant name
mcc
uint64
Merchant Category Code
city
string
Transaction city
country
string
Transaction country (ISO 3166 alpha-3)
cardId
string
Card identifier
cardCurrency
string
Cardholder's default currency
transactionCurrency
string
Currency required by the merchant
settlementCurrency
string
Settlement currency (default CHF)
transactionAmount
string
Amount in transaction currency
settlementAmount
string
Amount in settlement currency
originalPaidCurrency
string
Original paid currency
subEvent
string
Sub-event type (for crypto-backed card spending)
multiTokenList
array
Multi-token deductions (crypto-backed mode only)
multiTokenList[].token
string
Token symbol deducted (e.g., USDe)
multiTokenList[].value
string
Equivalent fiat value
multiTokenList[].amount
string
Token amount deducted
multiTokenList[].tokenAddress
string
Token contract address
reason
string
Failure/rejection reason (present only on failed transactions)
FIAT_DEPOSIT (cash deposit / bank pay-in)
Fiat deposit via bank transfer.
accountHolder
string
Depositor account holder name
bankName
string
Bank name
ref
string
Bank reference number
account
string
Bank account number / IBAN
fee
string
Fee amount (if applicable)
contactId
string
Contact identifier
FIAT_WITHDRAW (cash payout / bank withdrawal)
Fiat withdrawal via bank transfer.
txId
string
Internal transaction ID
bankRef
string
Bank reference number
contactId
string
Contact identifier
accountHolder
string
Account holder name
bankName
string
Bank name
account
string
Bank account number / IBAN
fee
string
Fee amount (if applicable)
purposeId
string
Purpose of payment ID
The details may also include a bankMetadata object with settlement tracking:
bankMetadata.initiate.hash
string
Initiation transaction hash
bankMetadata.initiate.timestamp
int64
Initiation timestamp
bankMetadata.initiate.txHashUrl
string
Block explorer URL
bankMetadata.payout.hash
string
Payout transaction hash
bankMetadata.payout.timestamp
int64
Payout timestamp
bankMetadata.payout.txHashUrl
string
Block explorer URL
bankMetadata.return.hash
string
Return transaction hash (if returned)
bankMetadata.return.timestamp
int64
Return timestamp
bankMetadata.return.txHashUrl
string
Block explorer URL
bankMetadata.bic
string
BIC / SWIFT code
bankMetadata.UETR
string
Unique End-to-end Transaction Reference
bankMetadata.creditor.country
string
Creditor country
bankMetadata.debitor.country
string
Debitor country
ONRAMP (fiat to crypto)
Convert fiat token to crypto asset. May involve cross-chain bridging.
inputChainId
string
Source chain ID (where fiat token lives)
inputToken
string
Input token address (e.g., USD24)
inputAmount
string
Fiat input amount
outputChainId
string
Destination chain ID
outputToken
string
Output token address (e.g., USDC, ETH)
expectOutputAmount
string
Expected output amount (quote)
actualOutputAmount
string
Actual output amount (after execution)
expectUsdcAmount
string
Expected intermediate USDC amount
actualUsdcAmount
string
Actual intermediate USDC amount
guid
string
Cross-chain unique identifier (LayerZero GUID)
destination
object
Destination chain transaction info
destination.status
string
Destination chain transaction status
destination.txHash
string
Destination chain transaction hash
destination.txTime
int64
Destination chain transaction time
retryHistory
array
Array of prior destination attempts (same shape as destination)
retryStatus
string
can_retry / retrying / retry_success / retry_fail / no_retry / cancelled
balance
string
Balance after transaction
partnerId
string
Partner identifier
partnerRefId
string
Partner reference ID
Other types
Type
detailsJson
UNKNOWN
No structured details. May contain arbitrary JSON or be empty.
12.4 Identifying refund transactions
Every transaction record carries a refundType field that tells you whether the transaction is a refund and what kind:
refundType
Meaning
"" (empty string)
Not a refund (default).
BANK_REFUND
Bank transfer refund. UR returned funds from a system bank account (fromURId in 9101/9102/9103).
CARD_REFUND
Card spending refund. UR returned card spending funds to the user via a P2P transfer from account 9113.
CARD_REVERSAL
Card authorization reversal. The card network reversed a prior authorization, or UR reversed from the card-booked account (9110).
Use refundType as the primary discriminator.
Rule 1: bank transfer refund (BANK_REFUND)
A P2P transfer where fromURId is one of the UR system bank accounts:
fromURId
Account role
9101
Bank deposit (mint) account
9102
Bank payout account
9103
Sundry / failed transaction account
Rule 2: card spending refund (CARD_REFUND)
A P2P transfer where UR's card settlement system returned funds to the user:
fromURId
Account role
9113
Card spending refund account
Rule 3: card authorization reversal (CARD_REVERSAL)
Appears in two forms:
Card network reversal:
type = MARQETA_AUTHORIZE,direction = IN. TheoriginAuthorizationIdfield indetailsJsonreferences the original card authorization.Card-booked account reversal:
type = INTERNAL_TOKEN_TRANSFER,direction = IN,fromURId = 9110(on-chainCARD_BOOKEDconstant).
Summary
Refund scenario
refundType
type
fromURId
Bank transfer refund
BANK_REFUND
INTERNAL_TOKEN_TRANSFER
9101 / 9102 / 9103
Card spending refund
CARD_REFUND
INTERNAL_TOKEN_TRANSFER
9113
Card reversal (card network)
CARD_REVERSAL
MARQETA_AUTHORIZE
N/A
Card reversal (on-chain)
CARD_REVERSAL
INTERNAL_TOKEN_TRANSFER
9110
12.5 Partner-level transaction history
Query all transactions across all users under your partner account. This is a partner-scoped endpoint and does not require X-Ur-Id or X-External-User-Id headers.
Method
POST
Path
/api/fma/v1/partner-transactions
Headers
Partner Auth Headers (no user identity required)
Request body:
The request accepts the same fields as §12.1 Fetch transaction history: pageSize, type, txTypes, currencies, direction, status, chainId, tokenSymbol, minAmount, maxAmount, fromTimestamp, toTimestamp, cursorTimestamp, cursorId.
Single-record lookups (id, txHash, reqId) are not supported on this endpoint. Use the user-scoped §12.1 or §12.2 endpoints instead.
Response uses the same structure as §12.1 (data.items[], data.hasNextPage, data.nextCursor). Each item includes the urId field so you can identify which user the transaction belongs to.
12.6 Delegator vault transaction history
Query transactions for the Delegate Vault account associated with your partner. The vault account's urId is resolved from your partner configuration (delegateVaultId).
Method
POST
Path
/api/delegator/v1/transactions
Headers
Partner Auth Headers (user identity headers are ignored; vault identity is resolved from config)
Request body:
The request accepts the same fields as §12.1 Fetch transaction history, including pagination cursors, type filters, and single-record lookups (id, txHash, reqId).
Response uses the same structure as §12.1. All returned transactions belong to the vault account.
13. Webhooks
Webhooks are the asynchronous delivery channel for transaction settlement updates. The Partner should subscribe to the transaction event for Off-ramp, FX, On-ramp, Fiat Deposit, and Payout settlement.
Card-Mode-specific webhooks (post-swipe card transaction,
prefund.balance.alert) are documented in API Reference: Card Mode: Crypto Backed.
13.1 Webhook envelope
13.2 Webhook signature verification
UR signs webhook requests with EIP-191.
Partner verification steps:
Read the exact raw request body string (do not re-serialize).
Recover the signer address using the body and
X-Api-Signature.Accept the event only if the signer address matches the UR public key provided out of band.
See Signature and Verification for the canonical recovery algorithm.
13.3 Retry and idempotency
The Partner should return
HTTP 200within 10 seconds.UR retries non-200 or timed-out webhook deliveries up to 3 times, with a 5-minute interval.
Use
data.txHashor the transactiondata.idas the idempotency key on the Partner side.
13.4 Transaction event
The transaction event uses the same transaction data structure as /api/fma/v1/transactions (§12.1). Route by data.type; see the type table in §12.1 for the canonical set.
14. Implementation checklist
Before going live:
Store both
externalUserId(Partner side) andurId(UR side) after onboarding.For user-scoped APIs, send at least one of
X-External-User-IdorX-Ur-Id(sending both is allowed; UR resolves byX-Ur-Idfirst).Sign
GETrequests with the raw query string, and sign non-GETrequests with the raw body, then appendurId:{X-Ur-Id}externalUserId:{X-External-User-Id} {X-Api-Deadline}.Keep
reqIdstable across retries forPOST /api/fma/v1/fx-exchange,POST /api/fma/v1/internal-transfer,POST /api/fma/v1/submit-payout,POST /api/fma/v1/onramp, andPOST /api/fma/v1/onramp-swap.Treat webhook delivery as at-least-once and implement idempotency keyed on
data.txHashordata.id.Reconcile transaction history through
POST /api/fma/v1/transactions; usetxHash,id, orreqIdon that same endpoint for exact lookup.Verify every webhook with EIP-191 recovery against UR's public key (§13.2).
If the Partner is enabling Card Mode: Crypto Backed, complete the additional checklist in API Reference: Card Mode: Crypto Backed.
15. Reference docs
Integration Guide: Account Mode, Card Mode, KYC Mode decisions.
API Reference: Card Mode: Crypto Backed: the Partner-side integration surface for Card Mode: Crypto Backed (authorization callback, Prefund Account, card-related webhooks).
API Reference: External Wallet Access Mode: the other Account Mode.
Last updated