---
url: /docs/fiat/payins.md
description: >-
  Create a payin from a payin quote, display the right payment instructions per
  method, and track it to completion
---

A payin is the object that actually moves money: it consumes a payin quote and tells BlindPay to start waiting for the customer's deposit. Everything about the payment (amount, method, fees, destination) was already locked in when you created the [payin quote](/fiat/payin-quotes); the payin itself takes a single field.

## How it works

```
payin quote (pq_...) -> create the payin -> display instructions to the payer -> BlindPay detects the deposit -> settles
```

A payin quote expires 5 minutes after creation, so create the payin before then. Once created, a payin cannot be canceled: if the payer never sends the money, the payin simply stays `processing` until it is cleaned up on BlindPay's side.

You also need a customer with a blockchain wallet or a managed wallet, and a [payin quote](/fiat/payin-quotes) (`pq_...`).

## Create a payin

Replace `pq_000000000000` with the payin quote you generated previously.

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

### Response

The response carries the payment instructions for whichever `payment_method` was set on the quote. Only the field relevant to that method is populated; the rest are `null`.

```json
{
  "id": "pi_000000000000",
  "status": "processing",
  "pix_code": null,
  "memo_code": "8K45GHBNT6BQ6462",
  "clabe": null,
  "partner_fee": 0,
  "receiver_id": "re_000000000000",
  "receiver_amount": 9900,
  "payment_method": "ach",
  "sender_amount": 10000,
  "billing_fee_amount": null,
  "transaction_fee_amount": 100,
  "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 Letterman Drive, Building A, Suite A4-700",
      "address_line_2": "San Francisco, CA 94129"
    }
  },
  "tracking_transaction": { "step": "processing" },
  "tracking_payment": { "step": "on_hold" },
  "tracking_complete": { "step": "on_hold" },
  "tracking_partner_fee": { "step": "on_hold" }
}
```

## What to display per method

| `payment_method` | What to show the payer | Field(s) in the response |
| --- | --- | --- |
| `ach` | `memo_code` plus BlindPay's bank details, so the deposit can be matched to this payin | `memo_code`, `blindpay_bank_details` |
| `wire` | Same as `ach` | `memo_code`, `blindpay_bank_details` |
| `pix` | The Pix code as copyable text or a QR code | `pix_code` |
| `spei` | The CLABE number | `clabe` |
| `transfers` | The account number (CVU, CBU, or Alias, see `type`) | `tracking_transaction.transfers_instruction.account`, `tracking_transaction.transfers_instruction.type` |
| `pse` | The payment link | `tracking_transaction.pse_instruction.payment_link` |

## Monitoring window

Once created, a payin waits for the fiat to actually land before it converts anything. On development instances every payin auto-completes about 30 seconds after creation, regardless of method. On production, the wait depends on the rail:

| `payment_method` | Currency | Typical arrival window |
| --- | --- | --- |
| `ach` | USD | up to 5 business days |
| `wire` | USD | up to 5 business days |
| `pix` | BRL | up to 5 minutes |
| `spei` | MXN | up to 5 minutes |
| `transfers` | ARS | up to 5 minutes |
| `pse` | COP | up to 5 minutes |

See [cut-off times](/kb/cut-off-times) for the full settlement-window reference.

## Status lifecycle

| `status` | Meaning | Terminal? |
| --- | --- | --- |
| `processing` | Waiting for the deposit to arrive, or converting/sending stablecoins once it has | no |
| `on_hold` | Held for manual review (risk or compliance) before continuing | no |
| `completed` | Stablecoins delivered to the destination wallet | yes |
| `failed` | The payin did not go through | yes |
| `refunded` | The deposit was returned to the sender | yes |

Each `tracking_*` object on the payin (`tracking_transaction`, `tracking_payment`, `tracking_complete`, `tracking_partner_fee`) exposes a finer-grained `step` (`processing`, `on_hold`, `pending_review`, `completed`) for the corresponding stage, useful for building a detailed status view.

## Testing

On development instances every payin auto-completes about 30 seconds after creation. Force a specific outcome instead by setting the payin quote's `request_amount` to one of these sentinel values:

| `request_amount` (minor units) | Result |
| --- | --- |
| `66600` ($666.00) | `failed` |
| `77700` ($777.00) | `refunded` |
| any other amount | `completed` after the normal ~30 second delay |

## Webhooks

| Event | Fires when |
| --- | --- |
| `payin.new` | The payin is created |
| `payin.update` | The payin's status or tracking data changes |
| `payin.complete` | The payin reaches `completed` |

See [webhooks](/learn/webhooks) for signature verification and payload details.

## Related

* [Payin quotes](/fiat/payin-quotes): lock the amount, method, and fee split before creating a payin
* [Bank transfer in](/fiat/receive): the end-to-end receive flow
* [Virtual accounts](/fiat/virtual-accounts): give a customer their own dedicated account instead of a memo code
* [Webhooks](/learn/webhooks): event payloads and signature verification
* [Cut-off times](/kb/cut-off-times): settlement windows by payment method
