Prerequisites
Your program must be configured with theRETAIN routing policy.
The routing policy determines what happens to funds when they arrive in a balance, and it is set during enrollment:
RETAIN— deposited funds stay on the balance until you move them. This is what makes manual withdrawals possible.SWEEP— deposited funds are automatically forwarded to a preconfigured credit instrument. Meridian initiates the withdrawal for you, so there is nothing to trigger manually.
routing_policy entry in the Information Request during onboarding:
- an enrollment in
ACTIVEstatus — see Onboarding your customer - an access token for the program context, with the
transactions:createscope - a funded balance to withdraw from
- the payment instrument IDs for both sides of the movement (see below)
Identifying the two instruments
Every intent moves funds from a debit instrument to a credit instrument. Both are identified by payment instrument ID.Debit instrument: the account ID
The debit instrument ID is the accountid. Call GET /v1/accounts to list the customer’s accounts, or GET /v1/accounts/{accountId} for a single one, and pass that id as debitInstrumentId:
GET /v1/accounts
The debit instrument must be a balance. Passing any other instrument type returns
400 with the message Debit instrument must be a balance. Both instruments must also belong to the authenticated caller, or the request returns 403.Credit instrument: provided by Meridian after onboarding
The credit instrument ID is provided by Meridian once your program has been onboarded. Your withdrawal destination is registered as part of program configuration — for an external wallet, from thedestination_external_wallet_account_number entry you submit in the Information Request. Meridian creates a payment instrument for that destination and gives you its ID once the program is configured.
Unlike the debit side, this ID is not returned by the accounts API. A RETAIN account reports only the policy type, with no destination:
GET /v1/accounts/{accountId}
Making a withdrawal
1
Create the transaction intent
Call
POST /v1/transaction-intents with both instruments and the amount. The response is a preview: it returns the exchange rate, the converted amount, and any fees, but no funds move yet.2
Review the quoted rate
Show the customer the quoted
exchangeRate, creditAmount, and fees from the intent so they can confirm before committing.3
Commit the intent
Call
POST /v1/transaction-intents/{transactionIntentId}/commit to execute the movement. This creates the transaction and returns the intent with a relatedTransactionId.4
Track the transaction
Use the returned
relatedTransactionId with GET /v1/transactions/{transactionId}, and consume transaction webhooks, to follow the withdrawal to completion.Step 1: Create the intent
fixedSide tells Meridian which side of the conversion your amount refers to. Use DEBIT to specify how much leaves the balance, or CREDIT to specify how much should arrive at the destination.
Request
CREATED status:
Response (201)
id — you need it to commit.
Step 2: Commit the intent
Commit with the sameidempotencyKey you used to create the intent:
Request
Response (200)
relatedTransactionId is the transaction Meridian created. Committing the same intent again returns the same result rather than creating a second transaction.
Intent status values
An intent holds a quoted exchange rate, so it does not stay valid indefinitely. If a customer takes too long to confirm, the intent reaches
EXPIRED and committing it returns an error — create a new intent to get a fresh rate.
The intent does not return an expiry timestamp, so treat the quote as short-lived and commit promptly after the customer confirms.
Idempotency
TheidempotencyKey is required on both calls, and the key you commit with must match the key the intent was created with. Use one unique key per withdrawal attempt — generated from your own reference for the withdrawal — and reuse it if you need to retry either call. This makes retries safe: a repeated request returns the original result instead of creating a duplicate withdrawal.
Recommended integration behavior
Your integration should:- confirm the program uses
RETAINbefore exposing withdrawal functionality in your UI - show the quoted
exchangeRate,creditAmount, andfeesto the customer before committing - treat the intent as short-lived, and request a new one when an intent reaches
EXPIRED - store the
idempotencyKeywith your own withdrawal record so retries reuse it - track completion through the
relatedTransactionIdand the transaction webhook, not the intent itself