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

# Payins (onramp)

> Turn a user's bank transfer into USDT or USDC, from quotation to webhook.

A payin turns a user's fiat bank transfer into stablecoins. The user pays Zapyd's collection account, and Zapyd delivers USDT or USDC on the network you choose.

**Base URL:** `https://sandbox.zapyd.com/pis/api/v1`. Customer calls use `/cms/api/v1` and limit calls use `/ren/api/v1`.

## Before you start

* The customer is `VERIFIED`. See [Onboard customers](/guides/customers/overview).
* Your [webhook URL](/guides/development-and-testing/webhooks) is registered.
* Your organization has a delivery wallet for each asset and network you use. Zapyd sets this up during onboarding.

## Flow

```mermaid theme={null}
sequenceDiagram
    participant App as Your backend
    participant Z as Zapyd
    participant U as User's bank
    App->>Z: GET /payin/limits/{customer_id}
    App->>Z: POST /payin/quotation
    Z-->>App: rate, fees, deposit_instructions, expiry_time
    App->>U: User transfers the exact amount
    U-->>App: Transfer reference (UTR)
    App->>Z: POST /payin/initiate (transaction_reference_id)
    Z-->>App: PROCESSING
    Z-->>App: PAYIN webhook: SUCCESS + transaction_hash
```

## 1. Read the configuration

[`GET /payin/configuration`](/api-reference-exchange/endpoint/payin/config/configuration) returns, for each fiat, the supported assets and networks, the crypto minimum and maximum per network and payment method, settlement times, and the risk parameters you must send. Cache it and refresh it daily.

```json theme={null}
{
  "inr": {
    "supported_assets": ["usdt", "usdc"],
    "supported_networks": { "usdt": ["tron", "polygon"], "usdc": ["polygon"] },
    "coin_limits": {
      "usdt": {
        "tron": { "imps": { "min": 28, "max": 5563 }, "upi": { "min": 28, "max": 1113 } }
      }
    },
    "required_risk_parameters": ["ip_address", "device_id", "suspicious_activity_report", "law_enforcement_agency_report"],
    "payment_methods": {
      "imps": { "min_settlement_time": 1, "max_settlement_time": 30 },
      "upi": { "min_settlement_time": 1, "max_settlement_time": 30 }
    }
  }
}
```

## 2. Check the customer's limit

[`GET /ren/api/v1/payin/limits/{customer_id}`](/api-reference-exchange/endpoint/payin/config/edd-limits-\{customer_id}) returns the customer's daily limit, how much is left, and whether they need Enhanced Due Diligence.

| Result | What to do |
| - | - |
| `available_limit` covers the amount | Continue |
| Not enough limit, `is_edd_required: true` | Ask the user to complete [EDD](/guides/payments/limits-and-edd), then check again |
| Not enough limit, `is_edd_required: false` | Block the order until `full_limit_reset_timestamp` |

## 3. Create a quotation

Optionally show a live rate first with [`GET /payin/fetch-rate`](/api-reference-exchange/endpoint/payin/config/fetch-rate). When the user confirms, create a quotation with [`POST /payin/quotation`](/api-reference-exchange/endpoint/payin/quotation/quotation). It locks the rate until `expiry_time`.

<CodeGroup>
  ```json Request theme={null}
  {
    "customer_id": "8da49e5e-33d1-48b2-b107-eb34f851b2fd",
    "asset": "USDC",
    "fiat": "INR",
    "network": "polygon",
    "payment_method": "IMPS",
    "sending_amount": "10000",
    "risk_parameters": {
      "ip_address": "203.0.113.10",
      "device_id": "device-123",
      "suspicious_activity_report": "False",
      "law_enforcement_agency_report": "False"
    }
  }
  ```

  ```json Response (201) theme={null}
  {
    "status": true,
    "message": "Success",
    "data": {
      "id": "da43453c-f854-42ac-9a1e-619b37060bbc",
      "asset": "USDC",
      "fiat": "INR",
      "network": "polygon",
      "payment_method": "IMPS",
      "sending_amount": "10000.00",
      "rate": "91.91",
      "receiving_amount": "108.80",
      "fees": { "zapyd_fee": "0.00", "client_fee_fiat": "0.00", "tds": "0.00" },
      "deposit_instructions": {
        "account_number": "1234567890",
        "ifsc": "YESB0000001",
        "account_name": "Sandbox Technologies Pvt Ltd",
        "deep_link": "upi://pay?pa=testing-payin@ybl&am=10000"
      },
      "expiry_time": "2026-09-30T10:10:00Z"
    }
  }
  ```
</CodeGroup>

| Field | Notes |
| - | - |
| `payment_method` | INR: `IMPS` or `UPI`. USD: `ACH_PULL`, `WIRE`, `DEBIT_CARD` or `RTP` |
| `network` | Required. The network the crypto is delivered on, from `supported_networks` |
| `bank_id` | Optional. The customer's bank account the money comes from |
| `deposit_instructions` | Where the user pays. For UPI it includes a `deep_link`. See [UPI QR codes](/guides/country-guides/india/qr-integration) |

## 4. Collect the fiat

Show the user `deposit_instructions`, the exact `sending_amount` and the time left before `expiry_time`.

<Warning>
  Payins are rejected or refunded when the user pays from an account that isn't in their own KYC name, pays a different amount, or splits the payment. Tell the user this before they pay.
</Warning>

After the transfer, the user's bank gives them a reference. In India this is the 12-digit UTR for IMPS and UPI. See [Understanding UTR numbers](/guides/country-guides/india/understanding-utr).

## 5. Initiate the payin

Call [`POST /payin/initiate`](/api-reference-exchange/endpoint/payin/order/initiate) before the quotation expires.

```json theme={null}
{
  "quotation_id": "da43453c-f854-42ac-9a1e-619b37060bbc",
  "customer_id": "8da49e5e-33d1-48b2-b107-eb34f851b2fd",
  "client_reference_id": "payin-001",
  "transaction_reference_id": "412345678901"
}
```

`transaction_reference_id` is required for INR: 12 or 16 characters, not used by any other payin. It's optional for other fiats. The payin starts as `PROCESSING`.

## 6. Track the result

Zapyd matches the transfer, then delivers the crypto and sends a `PAYIN` webhook.

| Event | Meaning | Your action |
| - | - | - |
| `SUCCESS` | Fiat received and crypto delivered | Credit the user. Store `metadata.transaction_hash` |
| `FAILED` | The transfer couldn't be matched, for example `INCORRECT_UTR` or `PAYMENT_NOT_RECEIVED` | Don't credit. Show the reason |
| `REFUND_INITIATED`, `REFUNDED` | Zapyd is returning the fiat, for example `THIRD_PARTY_PAYMENT` or `INCORRECT_AMOUNT` | Don't credit. Tell the user the money is coming back |
| `ON_HOLD` | Held for review | Don't credit. Contact support with the payin `id` |

To poll instead, call [`GET /payin/{payin_id}`](/api-reference-exchange/endpoint/payin/order/\{payin_id}) no more than once a minute. [`GET /payin/history`](/api-reference-exchange/endpoint/payin/order/history) lists orders with filters.

## Where the crypto goes

Zapyd delivers the crypto to your organization's delivery wallet for that asset and network, set up during onboarding. The address is `wallet_address` on the payin. If no wallet is set up for the pair, the quotation fails with `Not configured for asset: X & network: Y`. Credit the user from that wallet in your own ledger.

## Test it in sandbox

1. Create a quotation, then initiate with any 12-digit `transaction_reference_id`.
2. Move the payin with [Mock Payin Status](/api-reference-exchange/endpoint/payin/order/mock-payin-status): `SUCCESS`, `ON_HOLD`, `REFUNDED` or `FAILED`.
3. Check that your webhook handler credits the user only on `SUCCESS`.
