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

# Depositing funds

> Bring value into an account through a payment, a deposit address, a virtual account or fiat at checkout.

Value arrives in a Universal Payment Account four ways, and the account is the destination in all four. Choose by whether the arrival is bounded to one obligation or open-ended against one customer.

## Choose a route

| Route | Choose it when | What the payer does | What your backend does |
| - | - | - | - |
| **Hosted payment page** | You know the amount and have something to reconcile it against | Opens a link and pays it | Creates one receive payment per obligation |
| **Reusable deposit address** | The same customer funds the same account repeatedly | Sends any amount to a saved address, any time | Issues the address once, then reads deposits |
| **Virtual bank account** | The customer holds fiat and funds from the bank they already use | Sends a bank transfer to their own account number | Checks eligibility, issues the account, shows the details |
| **Fiat at checkout** | A payer holds no stablecoin and is settling one specific amount | Picks a fiat method on the payment page | Nothing beyond creating the receive payment |

Stablecoin arrival runs on the supported networks, with deposit-address issuance paused on two of them. Virtual bank accounts are US dollars only and certified on staging. Fiat at checkout runs in the enabled provider's markets. [View supported regions and currencies →](/supported-regions-and-currencies)

<Warning>
  A receive payment is bounded: one amount, one expiry, one reference, one lifecycle that ends. A deposit address is open-ended: no amount, no expiry, no reference, and it never closes. Do not create a payment to obtain an address, and do not treat one arrival at a deposit address as one order.
</Warning>

A customer who saves the address from an expired payment will pay it again later, leaving you a late receipt against a terminal payment. In the other direction, a deposit carries nothing that ties it to an order, so two customers sending the same amount on the same day are separable only by transaction.

## Collect a known amount with a hosted payment page

Your backend sets the amount, nothing payer-facing can change it, and your reference travels with the payment to the ledger.

```bash theme={null}
curl -X POST https://staging-api-v2.stableyard.fi/v2/payments \
  -u "$APP_ID:$APP_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: deposit:order_1042" \
  -d '{
    "intent": "receive",
    "recipient": { "accountId": "acct_123" },
    "paymentAmount": {
      "amount": "25.00",
      "assetType": "crypto",
      "assetCode": "USDC",
      "chainId": 42161
    },
    "externalReference": "order_1042",
    "expiresInSeconds": 600
  }'
```

The payer opens `checkout.paymentUrl` and funds it. The page presents the methods that exact payment supports, and reopening the link resumes the same payment until it expires; it never creates a second one.

Your backend stores `payment.id` against your reference, keeps `checkout.clientSecret` out of logs and URLs, and waits for a terminal state. An optional `returnUrl` must match a URL registered in your Payment settings, and the return is a presentation step, never proof of payment. A cancelled or expired payment is unpaid; a deliberate retry is a new payment with a new key.

## Build your own checkout

The hosted page makes these calls for you. To render your own, send `checkout.clientSecret` as a bearer token on the checkout endpoints, never in a URL.

<Steps>
  <Step title="List the methods this payment supports">
    `GET /v2/public/payments/{paymentId}/payment-methods`. Each method reports whether it is `available`, with its `minimumAmount` and `maximumAmount`.
  </Step>

  <Step title="Select one">
    ```bash theme={null}
    curl -X POST https://staging-api-v2.stableyard.fi/v2/public/payments/payment_123/options \
      -H "Authorization: Bearer $CLIENT_SECRET" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Content-Type: application/json" \
      -d '{ "paymentMethodId": "crypto.direct" }'
    ```

    The response is a `pay_option_` option. Selecting it does not change the payment's obligation, recipient, settlement or fee snapshot. Only one option is `selectionStatus: selected` at a time; earlier attempts become `superseded`.
  </Step>

  <Step title="Send the exact deposit">
    The payment's `nextAction.depositInstructions` carries `address`, `chainId`, `tokenAddress`, `assetCode`, `decimals`, `amount` and `amountAtomic`. Anything other than that exact transfer is not recognised as this payment's funding.
  </Step>

  <Step title="Submit the hash only when the option asks">
    When `confirmationMode` is `transaction_hash_submission`, post the payer's hash to `POST /v2/public/payments/{paymentId}/transactions` with `optionId` and `transactionHash`. It returns `202`.
  </Step>
</Steps>

| Option field | Meaning |
| - | - |
| `paymentMethodType` | `crypto`, `fiat_onramp`, `card` or `account_balance` |
| `status` | `creating`, `pending`, `processing`, `succeeded`, `expired`, `failed`, `cancelled` |
| `execution` | `deposit_address`, `hosted_checkout`, `managed_authorization`, or null while it is not ready |
| `confirmationMode` | `provider_webhook`, or `transaction_hash_submission` when the payer must supply a hash |
| `sourceAmount` | The exact amount to deposit in the chosen source asset |
| `requiredAmountAtomic`, `paidAmountAtomic`, `remainingAmountAtomic` | Progress against the obligation, so partial receipts aggregate |
| `refreshable` | True only when the option expired or failed and no financial evidence was seen |

The method decides the confirmation mode. Direct EVM and Solana methods use `provider_webhook` for collection detection. Supported direct EVM escrow also accepts a hash through the [transactions endpoint](/api-reference/checkout/submit-transaction) so a reverted transfer can be detected; the hash does not itself credit the Payment. Direct Solana does not support that endpoint. Direct Movement uses `transaction_hash_submission`. Every returned Routing method uses `transaction_hash_submission`, whether the source is EVM, Solana, Movement, Tron or Bitcoin. A submitted hash is only a candidate: Stableyard still verifies the chain, token, recipient, amount, finality and that the hash has not been used before.

A failed option is replaced, not retried in place. `POST /v2/public/payments/{paymentId}/options/{optionId}/refresh` returns a newly quoted option, takes a new `Idempotency-Key` for each intended replacement, and is refused once any payment evidence has been observed. Quote deadlines are on [Conversion](/payments/conversion#read-the-quote-where-it-appears).

## Give a returning customer a deposit address

A deposit address is permanent for one account on one chain. Discover what can be issued, then issue.

```bash theme={null}
curl https://staging-api-v2.stableyard.fi/v2/deposit-networks \
  -u "$APP_ID:$APP_SECRET"
```

```bash theme={null}
curl -X POST https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/deposit-addresses \
  -u "$APP_ID:$APP_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: address:acct_123:v1" \
  -d '{ "chainIds": [42161, 8453] }'
```

Each address comes back with the assets it accepts. If any requested chain is paused, the whole batch fails before an address is provisioned, so check `live` on the network catalog first.

Show the address, its QR and the asset's minimum, then read arrivals with `GET /v2/accounts/{accountId}/deposits`. Each arrival is its own `Deposit` with its own lifecycle. Credit on `settled`, not on `detected`.

If a transfer has not shown up, `POST /v2/accounts/{accountId}/deposit-addresses/check` asks Stableyard to look for it. The evidence you send is only a candidate: Stableyard still verifies the address, chain, token, amount, finality and that the evidence has not been used before.

| Source chain | Manual check | Evidence |
| - | - | - |
| Arbitrum, Ethereum, Base, Polygon, BNB Chain | Yes | Transaction hash. `tokenAddress` and `logIndex` pick out one token transfer |
| Solana | Yes | Transaction signature |
| Movement | Yes | Transaction hash |
| Bitcoin | Yes | Transaction ID and confirmed output |
| Tron | No | Detected through the provider webhook only |

<Note>
  A transfer below the asset's minimum is detected and recorded as `ignored`. It is not credited and not returned, and it stays on the address. Show the minimum, from the network catalog, next to the address every time.
</Note>

## Issue a virtual bank account

A bank account number in the customer's own name, with no session and no per-transfer setup. Fiat sent into it is converted and delivered as stablecoin to the destination the account chose.

Read `GET /v2/accounts/{accountId}/onramp-bank-account-requirements` first; it answers `available: false` with a reason instead of failing. Issue against one of the `destinations` it returns, then show the details from the deposit-instructions endpoint, the only response carrying the full account number. It is marked no-store: do not log or cache it.

The customer completes an identity check once, then sends transfers from their own bank. Routing is fixed at issue: asking again with the same wallet and asset returns the existing account, and asking with a different one is refused with `409 conflict`. The calls and the response are on [On-ramp accounts](/concepts/on-ramp-accounts).

## Offer fiat at checkout

A fiat method inside a receive payment, for a payer who holds no stablecoin. The provider takes the fiat and delivers stablecoin into the same escrow, so the create call, the payment id and the events do not change.

Read the methods from the payment rather than assuming a fiat option exists, because availability depends on your entitlement and the payer's market. A fiat option carries its own quote deadline beside the payment's expiry, and the effective window is whichever lapses first. See [On-ramps](/payments/on-ramps).

## Credit on verified receipt

```mermaid theme={null}
flowchart LR
  A["Funds arrive"] --> B["Held pending verification"]
  B --> C["Receipt verified independently"]
  C --> D["Settled to the destination, net of fees"]
```

A receive payment fixes one escrow obligation at creation: one amount, one asset, one chain, one address. Every option satisfies that same obligation, whether direct, routed, hosted fiat or Vault-funded, so no funding attempt can add a second platform or partner fee. A deposit-address arrival is recorded as a `Deposit` instead, with no escrow and no obligation attached.

Collection advances only from a receipt that passes the chain, token, recipient, amount, success, finality and non-reuse checks. A provider returning success, a broadcast transaction, or a payer saying they paid is execution progress, not a credit. Valid partial receipts aggregate and reused evidence is rejected. Duplicates, late arrivals, overpayments and underpayments run through recovery rather than adjusting a total silently; see [the money edges](/payments/reconciliation#handle-the-money-edges).

Fees are frozen when the payment is created or the deposit is detected, and deducted from the verified amount. The destination is snapshotted at the same moment from the account's settlement profile. On a receive payment it is the immutable `settlement` object:

```json theme={null}
{
  "settlement": {
    "type": "settlement_destination",
    "settlementDestinationId": "destination_123",
    "destinationType": "connected_wallet",
    "destinationAddress": "0x1111111111111111111111111111111111111111",
    "chainId": 42161,
    "assetCode": "USDC",
    "tokenAddress": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
    "decimals": 6
  }
}
```

Checkout cannot change it, and a request you build never carries the recipient's wallet address or bank details. It is null for payment types that do not use a receive settlement destination. To change where an account settles, change its profile: see [Merchant settlement](/settlement/merchant-settlement).

`accepted` means funds were verified and settlement has not finished. `succeeded` means the destination was credited. The full list is on [Status codes](/status-codes).

<Card title="Next: Send a payment" icon="paper-plane" href="/payments/sending-payments">
  Move value out of an account to a wallet, an account or a bank.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.