Create and Accept Quotes
Use this page when you need the operational details behind the quote lifecycle: creating a quote, accepting it, reading it back, and understanding when payment instructions are returned.
What This Page Covers
This page documents the quote-first flow used by pricing-sensitive transactions such as:
EXCHANGEWITHDRAWALTRANSFERSWAPDIRECT_EXCHANGE- some explicit
DEPOSITflows
It also explains where deposit flows behave differently, because not every deposit starts with a client-created quote.
All examples use the Business API (
POST /api/v1/client/*withx-api-key/x-timestamp/x-signature). The same shape is used forPOST /api/v1/quotes/v2(Bearer) but this page is written for the client integration.
The Main Endpoints
Use these client endpoints for quote-driven flows:
POST /api/v1/client/quotes— create a quotePOST /api/v1/client/quotes/{id}/accept— accept and executeGET /api/v1/client/quotes/{id}— read back latest quote state
Typical Quote-First Flow
The normal sequence is:
- create the quote
- inspect pricing, fees, and expiry (
timeline.valid_until) - accept the quote before expiry
- collect
payment_detailsor continue fulfillment for wallet/internal flows - track the resulting transaction by webhook or
GET /api/v1/client/transactions/v2/{id}
In many corridors, quote acceptance is the point where the platform returns the payment details the payer must use.
Create A Quote
Send a POST /api/v1/client/quotes request with the pricing intent.
Required Fields (Client API)
Commonly Used Fields
-
beneficiary_idRequired for most payout-style flows (EXCHANGE,DIRECT_EXCHANGE, fiatWITHDRAWAL). -
narrationFree-text description for the payout. Returned asnarrationin the quote response. -
payment_methodoneof=FIAT CRYPTOOptional — inferred from currencies when omitted. If you send it, it must match inference or you get400 payment_method does not match currency pair; expected FIAT/CRYPTO. Stablecoin source requiresDEPOSIT/DIRECT_EXCHANGE; stablecoin target requiresWITHDRAWAL. -
payment_network(ETH,SOL,BSC,MATIC) Required for crypto-funded flows. -
payment_addressWallet address — required when target is a crypto withdrawal. -
user_tagUsed only forTRANSFER— recipient's handle instead ofbeneficiary_id.
Conditional / Rail-Specific Fields
-
payer_emailstring,emailRequired whenrail: "INTERAC_REQUEST". CADDEPOSIT/DIRECT_EXCHANGEpull collection (money request). Capped at1500 CADper request. Does not register a user. -
payer_namestringRequired whenrail: "INTERAC_REQUEST". Name printed on the Interac request email (the person being asked to pay). Max 120 characters. Does not register a user or start KYC. -
bank_code/phone_numberMobile Money rails (MOBILE_MONEY) andZAR_BANK_ACCOUNT.phone_numbermust be international format without+(e.g.255712345678). Use operator code fromGET /api/v1/client/banks/{country}?type=momoasbank_code. -
railOptional rail override. Validated perquote_type+ currency:ACH/WIRE/SWIFT— USDDIRECT_EXCHANGE,EXCHANGE,WITHDRAWAL(USD target)INTERAC_REQUEST— CADDEPOSIT/DIRECT_EXCHANGEsourceEFT— CADDIRECT_EXCHANGE/WITHDRAWAL/EXCHANGEtargetMOBILE_MONEY/ZAR_BANK_ACCOUNT— mobile money / ZAR corridors Invalidrail→400 rail is only supported for ....
Quote Types And What They Usually Mean
EXCHANGE— standard conversion with external beneficiaryWITHDRAWAL— money-out from wallet/balance to payout destinationDEPOSIT— money-in flow (often funds-first, see below)SWAP— internal or near-instant conversion (internal ledger, no external payout)DIRECT_EXCHANGE— payment-in plus payout in one flow, often with funding instructionsTRANSFER— user-to-user usinguser_tag(internal ledger)
Transfer Quotes And user_tag
For TRANSFER quotes, the destination is identified by user_tag instead of beneficiary_id.
Practical rules:
- send
quote_type: "TRANSFER",source_currency == target_currency(same-ledger move) - provide recipient's
user_tag - do not reuse your own handle as destination
- keep tag lowercase and alphanumeric
on_behalf_ofstill owns the quote; the receiver is resolved from the tag
Example:
CAD Interac Money Request
By default, CAD deposits collect with Interac e-Transfer Auto Deposit (INTERAC): the payer sends funds to the email in payment_details.interac_payment_info.
To pull instead, create the quote with rail: "INTERAC_REQUEST". After accept, the platform sends an Interac money request to payer_email. The email greeting uses payer_name (the person being asked to pay). The payer approves it in their banking app. Settlement credits the quote owner's wallet — for a business using on_behalf_of: "self", that is the business wallet. payer_email and payer_name do not register a customer or start KYC.
Use this rail for:
quote_type: "DEPOSIT"withsource_currency: "CAD"(wallet collection)quote_type: "DIRECT_EXCHANGE"with CAD source (collect CAD, then pay out the target)
Rules
payer_emailis requiredpayer_nameis required (max 120 characters). It is the name on the Interac email, not a registered usersource_amountmust be 1500.00 CAD or less- omit
rail(or do not setINTERAC_REQUEST) if you want the default Auto Deposit flow - amounts above 1500 CAD must use Auto Deposit (
INTERAC), not money request
Sample Request: CAD Deposit
POST /api/v1/client/quotes
Sample Request: CAD Direct Exchange
Same rail, payer_email, and payer_name, plus a beneficiary_id for the payout destination:
Create still only prices the quote. The money request is sent when you accept:
POST /api/v1/client/quotes/{id}/accept
Sample Accept Response
Show the payer payment_instructions and the email the request was sent to. Track completion with transaction webhooks; do not assume the payer has approved until the webhook confirms it.
Deposit Quotes: Explicit Vs Implicit
Explicit Deposit Quotes
Some corridors behave like normal quote-first flows:
- you create the quote
- you accept it
- the platform returns funding instructions
- the user sends funds
This pattern is common when the platform needs to generate or confirm deposit instructions before money arrives.
Implicit Deposit Quotes
Other corridors are effectively funds-first:
- the user sends money to an assigned account or address
- the deposit confirmation arrives
- the platform creates the internal deposit quote and transaction
- your system tracks the result through transactions and webhooks
This means a missing client-side POST /api/v1/client/quotes call does not always mean “no quote exists.” In some deposit flows, the platform creates the quote internally after the money movement starts. For the Business API, deposits via POST /api/v1/client/virtual-wallets + webhooks are more common than client DEPOSIT quotes.
Create Quote Example
This example shows a quote-first request for a payout-style flow:
Create Quote Response (201)
Expiry is 30 minutes (90 minutes for INTERAC). At this stage, the quote is priced but not yet committed. Fees are in fee_currency == source_currency.
Validation you will hit often:
422 insufficient wallet balance,422 amount outside limit bounds,429 same CAD payout amount/beneficiary ... wait at least 2 minutes,422 customer KYC is incompletefor CAD/GBP/EUR whenon_behalf_ofcustomer not verified.
Accept A Quote
Once the pricing is accepted, call:
POST /api/v1/client/quotes/{id}/accept
Inside a transaction the platform re-checks valid_until, reserves/deducts wallet funds, creates holds/ledger legs and may call the payout rail. Expired quotes are terminal — 422 quote has expired.
Acceptance may:
- reserve or deduct wallet funds
- return bank transfer / ACH / wire / Interac instructions
- return blockchain funding details for crypto withdrawals
- immediately complete for internal flows (
TRANSFER/SWAP→is_internally_completed: true)
Accept Quote Response (200)
For checkout-style corridors, fee_bearer / merchant_amount / fees_amount may be present.
Read A Quote Back
Use GET /api/v1/client/quotes/{id} to retrieve the latest quote state — same shape as create, with timeline.deposit_confirmed_at / completed_at populated as the quote progresses. Useful for refreshes, recovery, and reconciliation.
Quote Statuses You Should Expect
The main states you will see via client:
CREATED— quote exists and can still be acceptedAWAITING_PAYMENT— accepted, waiting for payer to fundPAYMENT_CONFIRMED— funding leg confirmedPROCESSING_PAYOUT— payout/delivery leg runningCOMPLETED— quote-driven transaction finishedEXPIRED— not accepted in time (valid_untilis authoritative)FAILED— quote or follow-on transaction could not completePENDING_COMPLIANCE_REVIEW/REVERSED— compliance hold / reversed (you may see these on cross-border corridors)
Use GET /api/v1/client/quotes/{id} status plus timeline for polling, but prefer webhooks for final state.
Limits, Fees And Expiry
valid_untilis30m(USD/CAD/EFT etc.) and90mwhen funding method isINTERAC. Respect it — old quotes should be recreated, not reused.rateis spread-adjusted and rounded;target_amount = source_amount * raterounded to 2 decimals. Fees use ceiling rounding to 2 decimals.- Per-corridor min/max enforced; outside →
422. - Wallet check is done optimistically then authoritatively under lock →
422 insufficient wallet balanceifavailable_balance < total_payable.
Practical Rules For Integrators
- Treat
origin_referenceas required forPOST /api/v1/client/quotes(max 40, unique). Persist it — it is your idempotency key. on_behalf_offor Business API must be"self"or a customer UUID that ownsbeneficiary_id; otherwise401/422.- Always persist
quote_idandtransaction_referencefrom accept;transaction_idis theGET /api/v1/client/transactions/v2/{id}key. - Do not assume all deposits are explicit quote-first — many are funds-first via virtual wallets/webhooks.
- Read
payment_details.typebefore deciding how to render funding instructions; handle all cases above, especiallyINTERAC_REQUESTvsINTERAC. - Prefer webhooks for final state changes instead of polling alone.
- Respect
valid_until; do not retry accept onexpired— create a new quote. - For CAD pull, send
rail: INTERAC_REQUEST+payer_email+payer_nameand keepsource_amountat or below 1500 CAD.on_behalf_of: "self"credits the business wallet without registering the payer.