Skip to main content
A linked bank on-ramp lets a user onboard to crypto straight from their bank account. They link the account once, and you pull funds via ACH debit and deliver crypto to a wallet. No wallet signature is needed; the authorization comes from the verified bank funding source. The flow is a short server-side sequence with one client-side bank-linking step:
1

Create a link token

From your backend, create a Plaid link token for the user.
Use linkToken with Plaid Link in your app, or send the user to hostedLinkUrl.
2

Complete linking

Plaid returns a public token and the selected account(s) on the client. Send them to your backend and complete the link.
3

Find the funding source and check limits

Linking creates a funding source. Wait for one with status: "active", then read its limits.
A funding source moves through pending, active, review_required, ineligible, or disabled. Only active sources can be debited. Check what the user can deposit:
4

Prepare the deposit

Prepare a quote. This returns the ACH authorization text to show the user, along with a preparationId you’ll use to commit.
quoteType is exact_input (you specify the USD debited) or exact_output (you specify the crypto delivered). network supports solana, ethereum, polygon, base, avalanche, and arbitrum; asset is USDC.
5

Show the authorization and create the deposit

Display the quote summary and the ACH authorization message to the user. Once they authorize, commit the deposit with the preparationId.
Spritz runs risk checks before any money moves. If a check blocks the deposit, the API returns 409 before pulling funds; prepare a new quote to retry, since a blocked attempt consumes its preparationId.
6

Track the deposit

The deposit response carries the full lifecycle. Track it by reading the deposit or by reacting to webhooks.
status moves through authorized, processing, partially_released, completed, returned, or failed. The ACH pull is tracked in debitStatus (authorized, submitting, submitted, settled, returned, failed) and the crypto delivery in releaseStatus.

When a deposit is blocked

Spritz runs every deposit through eligibility, limit, and risk checks. A block returns an error before any money moves — no ACH pull is attempted. Every deposit failure collapses onto a few problem type/status values, so branch on the code extension member: it is the discriminator. Both prepare and create can return: create can additionally return:

Risk decisions

At create, Spritz evaluates the deposit for ACH-return risk. Any outcome other than authorized returns 409 and does not create the deposit:
A block driven by customer-return risk can also disable the funding source (status: "disabled", statusReason: "risk_blocked"). Once that happens, deposits against that source return source_not_eligible until the user links a different account.

Funding source status

A funding source moves through these states, and only active can be debited. When it isn’t active, statusReason explains why. statusReason takes one of: ownershipMatchStatus (matched, mismatch, review_required, or null) reports the raw name-match result, independent of status.

Handling returns

ACH debits can be returned after the fact (for example, insufficient funds). When that happens, the deposit reports a returnCode and returnReason, and the achDebitReturn.created and achDebitReturn.updated webhooks fire. See ACH returns for the return codes, exposure signals, and how to reconcile.

On-ramp

Auto-converting deposit accounts for push funding.

Webhooks

Track deposit status and ACH returns.