Skip to main content
Fixed routes are standing payment routes: a dedicated deposit account or wallet address that is permanently wired to a destination. Once a route is created, every deposit that lands on it is automatically converted (if needed) and delivered to the configured destination — no per-payment API call required.

Overview

A fixed route binds together:
  • A source account provisioned by Infinite — either a virtual bank account (for fiat deposits) or a deposit wallet address (for crypto deposits)
  • A sweep target — the destination the funds are delivered to (a wallet, an external bank account, or a USD balance account)
  • The currencies and rails for the conversion (e.g. USD via wire in, USDC on Base out)
Share the deposit details with your customer once. From then on, each incoming deposit triggers an automatic sweep to the destination, for any amount, indefinitely.

Route Types

The route type is inferred from the source and sweep target account types: FIAT_TO_FIAT is inferred when the sweep target is the customer’s USD balance account. destinationRail is BALANCE, and there is no crypto leg. An external bank account as the sweep target is FIAT_TO_CRYPTO_TO_FIAT (fiat in, convert, fiat payout) — not a balance credit. See Fiat deposits into a USD balance.

Fixed Routes vs Transfers

Fixed routes and transfers move money through the same underlying engine — the difference is who initiates each payment and when. They are similar in every other way:
  • Each swept deposit creates a transfer. When a deposit lands on a fixed route, Infinite creates a transfer behind the scenes. It appears in GET /v1/transfers, has the same statuses and legs, and emits the same transfer.* webhooks — so your reconciliation and tracking logic is identical for both.
  • Same compliance gates. The customer must be verified and ACTIVE before a fixed route can be created, just like transfers.
  • Same fee model. Developer fees (developerFeeFixed, developerFeePercent) configured on the route are applied to every sweep. FIAT_TO_FIAT routes are the exception: the deposit settles into your balance at face value, so developer fees are rejected on create and on update.

Creating a Fixed Route

There are two creation endpoints depending on the source side. Both require the customer to be verified, and both support the Idempotency-Key header for safe retries.

Fiat deposits (virtual bank account)

Creates a virtual bank account in the customer’s name. The example below converts ACH deposits to USDC on Base. To credit a USD balance with no conversion, see Fiat deposits into a USD balance.
The response includes depositInstructions on the source account — bank name, account number, and routing number your customer uses to send fiat. USD ACH, wire, and SWIFT pay-ins can set depositMatching on create:
  • DEDICATED — unique account number. The sender does not include a payment reference, and depositInstructions.reference is omitted.
  • REFERENCE — shared account number. The sender must put sourceAccount.depositInstructions.reference on the payment (wire memo or ACH addenda). A payment without that reference is not applied to the route, even when it is the only route on the number.
  • Omit depositMatching to keep the default for the rails you send.
Send "depositMatching": "DEDICATED" for a unique account number, or "depositMatching": "REFERENCE" when the sender will include the reference. Retry REFERENCE with the same Idempotency-Key within 24 hours to get the same reference back.

Fiat deposits into a USD balance

Use the same endpoint when the sweep target is the customer’s USD balance account (vba_…, holdsBalance: true). Each deposit on the new virtual bank account credits that balance in USD. Nothing is converted to a stablecoin, and nothing is paid out to an external bank. Create infers type: FIAT_TO_FIAT. Open the balance account first (create a balance account) and use its id as sweepTargetAccountId. The customer must be ACTIVE. The balance account must belong to that same customer. The new pay-in and the balance must be held by the same banking partner, on an account number that is not already on the balance. depositMatching follows the rules above. The customer must be approved for that bank pay-in: USD_ACH_PAYIN and USD_WIRE_PAYIN, plus SWIFT_PAYIN when the list includes SWIFT. USD_BALANCE ready is what opens the balance. It does not by itself make this route available, and those pay-in capabilities can also become ready through a partner that cannot settle into the balance. When the pay-in and the balance are not on the same partner with dedicated coordinates, create returns This pay-in route cannot settle into a balance account. Listing more than one inbound rail without that bank pay-in approval returns Multiple inbound rails are only available for USD bank pay-in on a single deposit account. Include SWIFT only when you want international wires.

Request fields

destinationRail and destinationCurrency on this request do not select a chain or a stablecoin. Create replaces them with BALANCE and the balance currency, and stores type as FIAT_TO_FIAT. On PATCH /v1/fixed-routes/{id}, sending either field while the sweep target is a balance account is rejected (destinationRail and destinationCurrency cannot be set when the destination is a balance account). POST /v1/fixed-routes/validate does not infer the route type. Pass fixedRouteType: "FIAT_TO_FIAT". An omitted destinationRail then defaults to BALANCE. Validate does not apply the banking-partner check that create does, so a successful validate is not proof the pay-in can settle into the balance. Read type and destinationRail on the create response. A pay-in currency that does not match the balance returns Balance account currency must match the pay-in currency. A different banking partner, or deposit coordinates shared with the balance account, returns This pay-in route cannot settle into a balance account. Crypto deposit routes (POST /v1/fixed-routes/virtual-wallets) cannot credit a balance account (Balance accounts can only receive same-currency deposits from a fiat-to-fiat route.). You can point an existing fiat pay-in (FIAT_TO_CRYPTO) at the balance later: PATCH /v1/fixed-routes/{id} with sweepTargetAccountId set to the balance account, and without destinationRail or destinationCurrency. The route type flips to FIAT_TO_FIAT, destinationRail becomes BALANCE, and the deposit account number stays the same. The change is rejected while the route has transfers in flight. Crypto deposit routes and fiat sandwich routes cannot retarget to a balance account.

sourceRail vs sourceRails

One USD bank pay-in account can accept more than one inbound rail. Domestic wire is WIRE. More than one rail is only for a USD bank pay-in on a single deposit account. Anything else returns Multiple inbound rails are only available for USD bank pay-in on a single deposit account. Include SWIFT only when you want international wires. A list may only include ACH, WIRE, and SWIFT (Multiple inbound rails can only include ACH, WIRE, and SWIFT). A USD balance sweep does not accept other rails. sourceRail on the response is the representative rail: ACH, then WIRE, then SWIFT. Deposit instructions include a routing number when ACH or wire is listed. When SWIFT is listed they also include swiftBic and the customer’s legal address as beneficiaryAddress. A SWIFT-only list omits the routing number. A deposit is not swept when its inbound rail is missing or is not in sourceRails. An on-us credit onto that same account number (a transfer that stays inside the banking partner, not an external ACH, wire, or SWIFT) is the exception: it is accepted on every enforced list and is not added to sourceRails.

How this differs from a convert route

The swept deposit still creates a transfer you can read from GET /v1/transfers. flow is FIAT_TO_FIAT, destination.paymentRail is BALANCE, and source and destination currency are both usd. That transfer can show COMPLETED before the balance moves. Confirm the credit with balance.credited, or by reading the balance. Pay the balance out later with a transfer whose source rail is BALANCE — see Balance accounts.

Response

id is the new deposit account, not the balance account. Share depositInstructions with the sender. Read it back with GET /v1/fixed-routes/{id} or GET /v1/customers/{customerId}/fixed-routes. sharesBalanceAccountCoordinates is false on this route: the deposit account number is dedicated, so it can sit alongside the balance account. When a pay-in would share the balance account’s deposit coordinates, create is rejected instead.

Sandbox

POST /v1/sandbox/fixed-routes/{id}/simulate-deposit takes this route’s source account id (vba_payin123). On a route that lists sourceRails, pass rail (ACH, WIRE, or SWIFT) to choose the inbound rail. Omit rail to book the stored rail — ACH when the route accepts ACH and wire. A rail the route does not accept is rejected (rail must be one of …). Use a new reference for each rail.
amount is in major units. The call creates the sweep and credits the balance (balance.credited). Reusing reference returns the existing sweep and keeps the rail recorded the first time; a later rail on that reference is ignored. Account fund-in on the balance account credits that account directly and does not run this route.

Crypto deposits (deposit wallet)

Creates a dedicated Infinite Wallet (inw_, walletType: VIRTUAL) — a crypto mailbox, not a vwa_ virtual wallet and not a treasury vault. Crypto sent to that address sweeps to an external bank account (crypto-to-fiat) or another wallet (crypto-to-crypto):
The response includes the walletAddress on the source account — this is the permanent deposit address for the route. The public fixed-route id is that source account id (vba_… for fiat, inw_… for crypto), not a separate fr_ id.

Response

The fixed route ID is the same as the source account ID.

Validating Before Creation

Use POST /v1/fixed-routes/validate to check whether a route configuration is supported for a customer — currencies, rails, and customer eligibility — before creating it. For a USD balance sweep, pass fixedRouteType: "FIAT_TO_FIAT". Create infers that type; validate does not. See Fiat deposits into a USD balance.

Managing Routes

  • enabled — when set to false (via PATCH /v1/fixed-routes/{id}), deposits still land on the source account but no longer auto-sweep.
  • Updatable fields — fees, refund account, enabled, metadata, and sweepTargetAccountId. Changing the sweep target keeps the same deposit details — the new destination must satisfy the same rules as at creation, the change is rejected while the route has transfers in flight, and transfers already created keep the destination they were created with. Pointing a fiat pay-in at a USD balance account flips the route to FIAT_TO_FIAT and sets destinationRail to BALANCE. Developer fees are rejected on that destination, on create and on update. On this route, sourceRail / sourceRails can be updated in place; the account number stays the same.
  • refundAccountId — a customer wallet to return funds to if a sweep fails or fees exceed the deposit amount.
  • fixed_route.disabled webhook — emitted if Infinite disables a route because its source or sweep target account is no longer active.

Testing in Sandbox

Use POST /v1/sandbox/fixed-routes/{id}/simulate-deposit to simulate a deposit on the source account without moving real funds. Pass the source account id (vba_… or inw_…). The sweep runs end to end, creating a transfer you can track as usual — including crediting a treasury Infinite wallet used as the sweep target. On a fiat pay-in that lists sourceRails, including a USD balance sweep, pass rail (ACH, WIRE, or SWIFT) to choose the inbound rail. Omit it to book the stored rail — ACH when the route accepts ACH and wire. Use a new reference for each rail. ACH, wire, FedWire, and SWIFT simulations include source.originator (the inbound remitter) so GET responses and transfer.* webhooks match production. Override it with originator, or pass {} to leave it null. Crypto deposits omit originator unless you set it. A route with allowedSenderBanks does not receive that default remitter. To sweep, pass senderBank, or an originator that includes a bankName, routingNumber, or swiftBic matching an allowlist entry. A name or account number alone does not match. That request, and one that omits both fields, returns Fixed route sweep was skipped — route disabled, currency mismatch, sender bank not allowed, or unsupported corridor. Do not use POST /v1/sandbox/accounts/{id}/fund-in on an inw_. That endpoint only credits funding-method accounts (vba_, vwa_, pba_). Fund-in will not credit a treasury wallet or a crypto deposit mailbox, so it cannot be used to test withdrawals from an Infinite wallet. Simulate the inbound on the route, then POST /v1/transfers with the vault as source.

Next Steps

Transfers

One-off payments and how swept deposits are tracked

Fixed Routes API Reference

Create a virtual bank account fixed route

Balance Accounts

Open a USD balance, then pay it out

Simulate a deposit

Sandbox inbound rail on a fixed-route source account