Skip to main content

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:
  1. Create a Virtual Account
  2. Share deposit instructions with the customer
  3. Customer wires USD
  4. Track conversion status
  5. Show balance
Unlike off-ramps, you do not call 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
Field notes Response
Store the 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 the deposit_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 the conversion_id from the first webhook.

Option A: webhooks

Example event (INITIATED)
Example event (COMPLETED)
Status progression The on-ramp progression is: 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 using id + status. If no webhook arrives within a reasonable window, poll GET /v2/conversions/{conversion_id}.

Option B: polling

If webhook delivery fails, list conversions by account to find the record:
Then poll 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
UI recommendations:
  • Increase visible USDC balance by destination.amount from 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 id of the on-ramp VA from Step 1.
The response returns the 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.
To test failure paths, such as a deposit below the minimum, add the matching label to the identity before simulating. See On-ramp and off-ramp conversion simulations.