> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spritz.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# On-ramp: fiat to crypto

> Bring fiat into crypto with auto-converting deposit accounts.

An [on-ramp](/guides/definitions#on-ramp) converts fiat into crypto. There are two ways
to bring fiat in:

* **[Auto-ramp accounts](/guides/definitions#auto-ramp-account)** (this guide): a
  dedicated virtual bank account for the user. Anything paid into it over ACH, FedNow,
  wire, or SEPA auto-converts to crypto and lands at a wallet address. Good for
  recurring or third-party funding, like payroll or invoices.
* **[Linked bank on-ramp](/guides/use-cases/linked-bank-onramp)**: link the user's bank
  and pull funds on demand via ACH debit.

## Before you start

* Authenticate as an integrator and act on behalf of a verified user. See
  [Authentication](/guides/authentication) and [Onboarding](/guides/onboarding).
* Confirm the user's `fiat_to_crypto` capability is `active` — read it from
  `GET /v1/users/me`, don't assume it from KYC status. US users typically see
  `requirements_needed` with a `terms_acceptance` requirement first: send the user
  through the requirement's `actionUrl` hosted flow, then record the acceptance with
  `POST /v1/users/me/terms`.

<Note>
  The provider activates the user's account **asynchronously** after terms acceptance.
  The capability can already read `active` while the provider is still catching up — for
  up to about 30 seconds, creation fails with `400` and detail `The customer account is
      not active`. Retry with backoff instead of surfacing it to the user.
</Note>

## Create an auto-ramp account

```bash theme={null}
curl -X POST https://platform.spritz.finance/v1/auto-ramp-accounts \
  -H "Content-Type: application/json" \
  # plus your integrator signing headers and the user's Authorization (see Authentication)
  -d '{
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "network": "base",
    "token": "USDC"
  }'
```

```json theme={null}
{
  "id": "6a43ac369288351e982157b9",
  "status": "active",
  "network": "base",
  "address": "0x742d35cc6634c0532925a3b844bc9e7595f2bd18",
  "token": "USDC",
  "currency": "USD",
  "depositInstructions": {
    "type": "us",
    "bankName": "Lead Bank",
    "bankAddress": "1800 North Pole St., Orlando, FL 32801",
    "bankRoutingNumber": "101019644",
    "bankAccountNumber": "1234567890",
    "paymentRails": ["ach_push", "fednow", "wire"]
  }
}
```

The deposit instructions are what the user (or their employer, or their customer) sends
money to. Anything that arrives auto-converts to `token` on `network` and settles to
`address` — no further API call is needed to trigger the conversion.

Three things that catch people out:

1. **The account is keyed to the (token + network + destination wallet) triple.** You are
   not creating "a virtual account for the user" — you're creating "a virtual account for
   USDC on Base going to `0xABC…`". Change the destination wallet and you get a whole new
   virtual account with new deposit details; the old one doesn't follow. Store the
   destination address alongside the account in your database, and when a user changes
   their receiving wallet, expect to create a new auto-ramp account and update the
   deposit instructions everywhere you've shown them — including any saved payee the user
   set up at their own bank. That last one is the real-world failure mode: a recurring
   transfer pointed at the old details.
2. **The beneficiary is the end user**, not Spritz and not you. The user is sending money
   to an account in their own name — exactly what their bank expects to see. Present it
   that way in your UI.
3. **Check `status` before showing deposit instructions.** Only `active` accounts should
   receive money. Confirm it on every render.

### Valid combinations and sizes

Call `GET /v1/on-ramps/supported-pairs` for the network, token, and rail combinations the
user's region supports, with each pair's `minAmount` — don't hardcode the list. An
unsupported combination returns `400` (`Unsupported Configuration`), and an
unsupported-region request tells you the same way.

To preview a deposit before the user sends it, call
`GET /v1/auto-ramp-accounts/{id}/estimate?amount=100`:

```json theme={null}
{
  "input": { "amount": "100.00", "currency": "USD" },
  "fees": { "total": "1.00", "currency": "USD", "breakdown": { "…": "…" } },
  "output": { "amount": "99.00", "token": "USDC", "network": "base", "address": "0x…" },
  "rate": { "value": "1", "source": "peg", "asOf": "2026-09-03T16:09:09.229Z" }
}
```

On-ramps are a flat 1% with no minimum (plus \$20 on wire-funded ones) — see
[End-user pricing](/guides/pricing).

## Track conversions

Each deposit that converts produces an on-ramp record. List with `GET /v1/on-ramps`,
read one with `GET /v1/on-ramps/{id}`, or react to [webhooks](/guides/webhooks):
`onramp.created`, `onramp.updated`, and `onramp.completed` fire as deposits are detected,
converted, and delivered.

| `status`              | Meaning                                    | What to do                                                                    |
| --------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
| `awaiting_payment`    | Deposit expected, not yet seen             | Show the deposit instructions; nothing to reconcile                           |
| `processing`          | Deposit arrived, conversion under way      | Transient — show "converting"                                                 |
| `partially_delivered` | Part of the crypto has landed              | Transient — keep waiting, don't re-credit yet                                 |
| `completed`           | Crypto delivered to the destination wallet | Terminal — credit the user from this record's `output`                        |
| `in_review`           | The deposit is under compliance review     | Show review state; wait for a webhook, don't retry                            |
| `failed`              | The conversion failed                      | Terminal — surface it and reconcile with support                              |
| `cancelled`           | Cancelled before conversion                | Terminal (note the double-l spelling — the off-ramp resource uses `canceled`) |
| `reversed`            | The delivery was reversed                  | Terminal — reconcile with support                                             |
| `refunded`            | The fiat was refunded                      | Terminal                                                                      |

Wire deposits settle the same banking day; ACH takes one to two banking days; FedNow is
near-instant. See [Settlement timing](/guides/timing).

<Warning>
  Settlement is not finality on ACH: a push payment can be returned after it settles, in
  windows running from a couple of banking days out to 60 calendar days (see
  [ACH returns](/guides/ach-returns) for the windows and buckets). A return after
  delivery surfaces as the on-ramp moving to `reversed` — so if you credit users on your
  side when an on-ramp completes, a later return is your exposure to think about.
</Warning>

## Sandbox testing

The sandbox simulates a deposit arriving at an auto-ramp account — no real funds move,
but the full conversion path runs and fires real `onramp.*` webhooks:

```bash theme={null}
curl -X POST https://sandbox.spritz.finance/v1/sandbox/auto-ramp-accounts/{id}/deposit \
  -H "Content-Type: application/json" \
  # plus your integrator signing headers and the user's Authorization
  -d '{ "amount": "10.00" }'
```

The deposit settles the on-ramp to `completed` immediately. Terms acceptance is also
headless in sandbox: `POST /v1/users/me/terms` accepts any opaque `agreementId`.

<Warning>
  The simulated on-ramp's `output.txHash` is **synthetic** — a real-looking hash that
  does not exist on-chain. Don't render it as an explorer link in your sandbox testing;
  gate explorer links on your environment.
</Warning>

See [Sandbox & Testing](/guides/sandbox).

## Integration checklist

* [ ] UI is driven from `capabilities`, and `terms_acceptance` is completed before
  account creation
* [ ] Creation retries (or defers) through the provider's asynchronous activation window
  instead of surfacing `The customer account is not active` to users
* [ ] Deposit instructions are only shown for `active` accounts
* [ ] The (token + network + wallet) triple is stored with the account, and a wallet
  change creates a new account plus new instructions everywhere they're saved
* [ ] The beneficiary is presented as the end user
* [ ] Valid pairs and minimums come from `GET /v1/on-ramps/supported-pairs`, not a
  hardcoded list
* [ ] `onramp.*` webhooks drive updates; `GET /v1/on-ramps` reconciles misses

## Related

<CardGroup cols={2}>
  <Card title="Linked bank on-ramp" icon="building-columns" href="/guides/use-cases/linked-bank-onramp">
    Pull funds from a linked bank account instead.
  </Card>

  <Card title="Off-ramp: crypto to bank" icon="arrow-right-from-bracket" href="/guides/use-cases/off-ramp">
    The reverse direction: crypto in, fiat out.
  </Card>

  <Card title="What we support" icon="globe" href="/guides/supported">
    Networks, tokens, and currencies.
  </Card>

  <Card title="Settlement timing" icon="clock" href="/guides/timing">
    How long each rail takes.
  </Card>
</CardGroup>
