---
url: /docs/fiat/quickstart-payin.md
description: >-
  Accept your first bank transfer and have BlindPay settle it to your customer
  automatically on a development instance.
---

This guide walks through the minimum steps to receive a fiat payment: accept the terms of service, create a customer, give that customer a stablecoin delivery target, quote the payin, and create it. On a development instance the deposit settles automatically about 30 seconds after you create the payin.

## Before you begin

You need a BlindPay account and an API key for a development instance. See [Instances](/learn/instances) and [API keys](/learn/api-keys).

### Accept terms of service

Every instance requires a terms of service acceptance before you can create customers. For testing, you can accept on behalf of the customer; in production, your customer should accept the terms themselves.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/e/instances/in_000000000000/tos \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "idempotency_key": "<your_uuid>"
  }'
```

Open the URL from the response in your browser, accept the terms, and copy the `tos_id` shown on the confirmation screen. You will pass this `tos_id` when you create the customer in the next step.

### Create a customer

Every payment flows through a customer that has completed KYC. This example creates an individual customer with standard KYC.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "tos_id": "to_000000000000",
    "type": "individual",
    "kyc_type": "standard",
    "email": "email@example.com",
    "tax_id": "12345678",
    "address_line_1": "8 The Green",
    "address_line_2": "#12345",
    "city": "Dover",
    "state_province_region": "DE",
    "country": "US",
    "postal_code": "02050",
    "ip_address": "127.0.0.1",
    "phone_number": "+13022006100",
    "proof_of_address_doc_type": "UTILITY_BILL",
    "proof_of_address_doc_file": "https://example.com/proof-of-address.jpg",
    "first_name": "John",
    "last_name": "Doe",
    "date_of_birth": "1998-01-01T00:00:00Z",
    "id_doc_country": "US",
    "id_doc_type": "PASSPORT",
    "id_doc_front_file": "https://example.com/passport-front.jpg",
    "selfie_file": "https://example.com/selfie.jpg"
  }'
```

Save the `id` from the response: this is your customer ID (`re_...`).

On development instances, KYC is approved automatically. In production, automated review for standard KYC takes about 60 seconds.

### Create a managed wallet

A payin needs somewhere to deliver the stablecoins once the fiat arrives, so it asks for a wallet ID rather than a bank detail. The simplest destination is a [managed wallet](/fiat/wallets): BlindPay generates the address and custodies the balance, so there is no external wallet to connect and nothing to sign.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/wallets \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "network": "base_sepolia",
    "name": "Quickstart Wallet"
  }'
```

Save the `id` from the response: this is your managed wallet ID (`bl_...`).

This wallet is settlement plumbing, not the part you build a UI around. If your customer wants the stablecoins delivered to a wallet they control instead, register a [blockchain wallet](/stablecoin/blockchain-wallets) and pass its `blockchain_wallet_id` on the quote. And if you want deposits to arrive without memo codes, create a [virtual account](/fiat/virtual-accounts) for the customer.

### Create a payin quote

A payin quote locks in how much fiat the sender sends and how much the customer receives before you create the payin. Pass `wallet_id` to target the managed wallet; BlindPay detects the delivery network from the wallet. This example quotes an ACH payin with the sender covering the fee, so `request_amount` is the amount the sender sends.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/payin-quotes \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "wallet_id": "bl_000000000000",
    "currency_type": "sender",
    "cover_fees": true,
    "request_amount": 10000,
    "payment_method": "ach",
    "token": "USDB"
  }'
```

`request_amount` is an integer in minor units, so `10000` here means $100.00. On development instances the token is always `USDB`, BlindPay's test stablecoin; in production use `USDC` or `USDT`. Save the `id` from the response: this is your payin quote ID (`pq_...`). You have 5 minutes to create the payin before the quote expires.

### Create the payin

Create the payin from the quote ID. This is what generates the bank details your end user pays into.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/payins/evm \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "payin_quote_id": "pq_000000000000"
  }'
```

The response includes `memo_code` and `blindpay_bank_details`. Share these with the end user so they know where to send the ACH transfer and how to reference it.

```json [Response]
{
  // ...
  "memo_code": "12345678",
  "blindpay_bank_details": {
    "routing_number": "121145349",
    "account_number": "621327727210181",
    "account_type": "Business checking",
    "beneficiary": {
      "name": "BlindPay, Inc.",
      "address_line_1": "8 The Green, #19364",
      "address_line_2": "Dover, DE 19901"
    },
    "receiving_bank": {
      "name": "Example Bank, N.A.",
      "address_line_1": "1 Example Plaza",
      "address_line_2": "San Francisco, CA 94129"
    }
  }
  // ...
}
```

If the customer has an enabled virtual account, the `memo_code` is ignored and the deposit goes straight to their dedicated account details instead. See [Virtual accounts](/fiat/virtual-accounts).

## What happens next

On a development instance, the payin settles automatically about 30 seconds after you create it. Two webhooks confirm it: `payin.complete` for the payin itself and `wallet.inbound` when the stablecoins land in the managed wallet. In production, BlindPay waits up to 5 business days for an ACH or wire deposit to arrive before cancelling the payin if nothing shows up.

Done. To receive another payment, create a new payin quote and repeat the last step.

## Related

* [Bank transfer in](/fiat/receive): full reference for every payment method, including Pix, SPEI, and PSE
* [Virtual accounts](/fiat/virtual-accounts): a dedicated deposit account whose deposits settle to the linked wallet
* [Pay out to bank](/fiat/send): send funds back out to a bank account
* [Webhooks](/learn/webhooks): handle payin and customer events in real time
