Goal
Fund a customer’s Bastion account with USDC by converting USD wired from their bank account, using an on-ramp conversion via a Virtual Account (VA).Scope: USD → USDC, Wire only, US customers only, first-party (customer wires from their own bank account).
Key entities
Flow overview
The steps to fund a stablecoin balance from a bank account are:- Create a Virtual Account
- Share deposit instructions with the customer
- Customer wires USD
- Track conversion status
- Show balance
POST /v2/conversions. Bastion auto-creates the conversion the moment it detects the incoming wire.
Prerequisites
- Customer has a verified identity (KYC or KYB) with
fiat_operations_enabled = true - Customer has an active Bastion account on a supported chain
- You have an API key and registered webhook
Step 1 – create a virtual account
Create a Virtual Account for the customer. Each VA links to a specific chain, asset, and destination wallet; create one VA per chain and asset you want to support. Request
Response
id as virtual_account_id so you can reference this VA later. You can retrieve it again at any time from the GET /v2/identities/{identity_id}/virtual-accounts list endpoint.
Step 2 – share deposit instructions with the customer
Present thedeposit_instructions from Step 1 to the customer so they can initiate a wire from their bank.
Fields to surface in your UI
The
bank_account_number is unique to this VA. The customer does not need to include a memo or reference. The account number alone routes the deposit to the correct VA.
Step 3 – customer wires USD
The customer initiates a domestic wire from their bank to the deposit instructions shared in Step 2. When the wire lands at Bastion’s banking partner, Bastion detects it and auto-creates a conversion record. No API call is required from your platform at this step. Testing in sandbox? See Test the on-ramp in sandbox.Step 4 – track conversion status
Once Bastion detects the wire, it creates the conversion and fires webhooks as the conversion progresses. You learn theconversion_id from the first webhook.
Option A: webhooks
Example event (INITIATED)INITIATED → PROCESSING → PAYOUT_INITIATED → PAYOUT_IN_PROGRESS → COMPLETED
Handle statuses:
Webhook reliability: Failed deliveries are retried with exponential backoff (15 s base interval, 10 min max). Deduplicate usingid+status. If no webhook arrives within a reasonable window, pollGET /v2/conversions/{conversion_id}.
Option B: polling
If webhook delivery fails, list conversions by account to find the record:GET /v2/conversions/{conversion_id} for status updates.
Step 5 – show updated balance
After the conversion completes, refresh balances and show the on-ramp in the customer’s history. Refresh balances- Increase visible USDC balance by
destination.amountfrom the completion webhook - Display USD wired (gross), fees, USDC delivered (net), and status
- Show source (masked bank account details from the wire)
Fees
fees_total on the conversion reflects the incoming wire fee only. Bastion deducts the fee from the wired USD amount before delivering USDC.
Example: customer wires $500 → fees_total = 14.00 → destination.amount = 486.00 USDC delivered.
The fees array is populated from the moment the conversion is created (when the wire lands).
Developer fees are not supported yet.
Implementation notes
Test the on-ramp in sandbox
Sandbox Virtual Accounts can’t receive real deposits. To test the on-ramp end to end, simulate a deposit instead. Bastion then runs the same on-ramp flow a real deposit triggers. How a simulated deposit differs from a real one- No USDC is delivered, so the account balance doesn’t change. Use simulation to test status tracking and webhook handling, not funded balances.
- The simulation endpoint is available in sandbox only.
1
Simulate the deposit
Call the simulation endpoint with the customer’s account and the The response returns the
id of the on-ramp VA from Step 1.request_id of the on-ramp conversion the deposit starts.2
Find the conversion
Poll the lookup endpoint with the
request_id until it returns the conversion. The response includes the conversion’s id.3
Track the conversion
From here the conversion behaves like a real one: webhooks fire as it moves through each status, and you can poll
GET /v2/conversions/{conversion_id} with its id. Track its status as described in Step 4. Balances don’t change in sandbox, so skip the balance updates.