> ## Documentation Index
> Fetch the complete documentation index at: https://razorpay-881012b3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a Bill Payment | BBPS API | Razorpay

> Confirm a BBPS bill payment on the NPCI network after collecting money via the Razorpay Payment Gateway. Returns processing status with a bill payment id to poll for the final outcome.

<div style={{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"0.35rem 0.9rem",border:"1px solid rgba(128,128,128,0.28)",borderRadius:"0.5rem",padding:"0.45rem 0.75rem",margin:"0 0 1.25rem",fontSize:"0.875rem"}}>
  <span style={{fontWeight:600}}>Available in</span>
  <span>🇮🇳 India</span>
</div>

Use this endpoint to confirm a bill payment on the BBPS network after you have collected payment from the customer via the [Razorpay Payment Gateway (PG)](/docs/payments/payment-gateway). Razorpay acts as both the Payment Aggregator (PA) and the Customer Operating Unit (COU). You, the Agent Institution, orchestrate two steps: collect money via the PG, then call this API to confirm the bill settlement.

This is an asynchronous API. The response immediately returns a `processing` status with a bill payment `id`. Poll the [Fetch Bill Payment](/docs/api/bills/bill-payments/fetch-bill-payment) API or listen to webhooks until the status reaches a terminal state (`success` or `failed`).

<Warning>
  **Prerequisite**

  Call this API only after the Razorpay PG payment is captured. The `payments[].id` field must be a valid Razorpay payment id with a `captured` status. Calling this API before the PG payment is captured will result in a failed bill settlement.
</Warning>

<Info>
  **Idempotency**

  Pass a unique `X-Bill-Payments-Idempotency` header (4–36 characters, alphanumeric with hyphens, underscores or spaces) to safely retry requests without creating duplicate payments. A UUID v4 is recommended. The request body on retries must match the original request, else it is rejected as a `BAD_REQUEST_ERROR`.
</Info>

<Info>
  **Normal Pay vs Direct Pay**

  * **Normal Pay** - used when a bill is retrieved first via [Create a Bill Request](/docs/api/bills/bill-payments/create-bill-request). Pass `bill_request_id`.
  * **Direct Pay** - used when the biller's `bill_request_required` flag is `optional` or `not_supported` and you skip the bill retrieval step. Pass `biller_id` (or `gateway_biller_id`), `biller_data.account_holder` and `device` instead.
</Info>

<RequestExample>
  ```bash Normal Pay theme={null}
  curl -u [YOUR_KEY_ID]:[YOUR_KEY_SECRET] \
  -X POST https://api.razorpay.com/v1/bill_payments/payments \
  -H "Content-Type: application/json" \
  -H "X-Bill-Payments-Idempotency: 53cda91c-8f81-4e77-bbb9-7388f4ac6bf4" \
  -d '{
    "bill_request_id": "billreq_ERNEungCtXpZqM",
    "bill_pay_amount": 1015,
    "currency": "INR",
    "fees": {
      "app_convenience_fee": 10,
      "biller_convenience_fee": 5
    },
    "payments": [
      {
        "id": "pay_MbJ5AvwNpAkfLB",
        "amount": 600,
        "currency": "INR",
        "provider": "razorpay",
        "method": "card",
        "card": {
          "type": "credit",
          "last4": "0153",
          "auth_code": "Card"
        }
      },
      {
        "id": "pay_MbJ5AvwNpAkfLA",
        "amount": 430,
        "currency": "INR",
        "provider": "razorpay",
        "method": "upi",
        "upi": {
          "vpa": "gaurav.kumar@okhdfc.com"
        }
      }
    ],
    "bills": [
      {
        "bill_number": "820356722187",
        "amount": 1015,
        "currency": "INR"
      }
    ]
  }'
  ```

  ```bash Direct Pay theme={null}
  curl -u [YOUR_KEY_ID]:[YOUR_KEY_SECRET] \
  -X POST https://api.razorpay.com/v1/bill_payments/payments \
  -H "Content-Type: application/json" \
  -H "X-Bill-Payments-Idempotency: 53cda91c-8f81-4e77-bbb9-7388f4ac6bf4" \
  -d '{
    "biller_id": "biller_001",
    "gateway_biller_id": "TPOW00000MUM01",
    "customer": {
      "id": "rzp_cust_123",
      "name": "Gaurav Kumar",
      "mobile": "919000090000",
      "email": "gaurav.kumar@example.com"
    },
    "biller_data": {
      "account_holder": {
        "account_number": "XXXXXXX",
        "KEY": "VALUE",
        "KEY2": "VALUE2"
      }
    },
    "device": {
      "initiating_channel": "mobile",
      "mobile": "9999999999",
      "geocode": "12.9667,77.5667",
      "ip": "198.1.1.1",
      "mac": "00-0D-60-07-2A-F0",
      "os": "android",
      "imei": "490154203237518",
      "app": "amazon_pay"
    },
    "bill_pay_amount": 1015,
    "currency": "INR",
    "fees": {
      "app_convenience_fee": 10,
      "biller_convenience_fee": 5
    },
    "payments": [
      {
        "id": "pay_MbJ5AvwNpAkfLA",
        "amount": 600,
        "currency": "INR",
        "provider": "razorpay",
        "method": "card",
        "card": {
          "type": "credit",
          "last4": "0153",
          "auth_code": "Card"
        }
      },
      {
        "id": "pay_MbJ5AvwNpAkfLB",
        "amount": 430,
        "currency": "INR",
        "provider": "razorpay",
        "method": "upi",
        "upi": {
          "vpa": "gaurav.kumar@okhdfc.com"
        }
      }
    ],
    "bills": []
  }'
  ```
</RequestExample>

<ResponseExample>
  ```json Success theme={null}
  {
    "id": "bill_pay_xxxx",
    "entity": "bill_payment.payment",
    "status": "processing",
    "customer": {
      "id": "rzp_cust_123",
      "name": "Gaurav Kumar",
      "mobile": "919000090000",
      "email": "gaurav.kumar@example.com"
    },
    "biller_id": "biller_001",
    "gateway_biller_id": "TPOW00000MUM01",
    "gateway_transaction_id": "bbps_txn_id",
    "gateway": "bbps",
    "created_at": 1609459200
  }
  ```

  ```json Failure theme={null}
  {
    "error": {
      "code": "BAD_REQUEST_ERROR",
      "description": "The payment method used is not supported by this biller.",
      "source": "biller",
      "step": "bill_payment",
      "reason": "biller_unavailable",
      "metadata": {}
    }
  }
  ```
</ResponseExample>

## Request Parameters

<ParamField body="bill_request_id" type="string">
  Bill request id returned by [Create a Bill Request](/docs/api/bills/bill-payments/create-bill-request). Mandatory for Normal Pay. When omitted, the request is treated as Direct Pay and `device` and `biller_data.account_holder` become mandatory. For example, `billreq_ERNEungCtXpZqM`.
</ParamField>

<ParamField body="biller_id" type="string">
  Razorpay's internal biller id. Required for Direct Pay if `gateway_biller_id` is not provided.
</ParamField>

<ParamField body="gateway_biller_id" type="string">
  NPCI's biller id. Required for Direct Pay if `biller_id` is not provided. For example, `TPOW00000MUM01`.
</ParamField>

<ParamField body="bill_pay_amount" type="integer" required>
  Actual bill amount for which the bill payment is raised, in paise. Excludes fees. Must satisfy the biller's `payment_config.amount_exactness` rule (`exact`, `exact_or_above`, `exact_or_below` or `any`). This value can be less or more than an individual bill amount. For example, `1015` represents ₹10.15.
</ParamField>

<ParamField body="currency" type="string" required>
  Currency in which the bill payment is initiated. Defaults to `INR`.
</ParamField>

<ParamField body="fees" type="object" required>
  Fee breakdown. Pass `0` for components that do not apply.
</ParamField>

<ParamField body="fees.app_convenience_fee" type="integer" required>
  Merchant/app convenience fee charged to the customer, in paise. Not part of NPCI standard. Platform-specific. Pass `0` if not applicable.
</ParamField>

<ParamField body="fees.biller_convenience_fee" type="integer" required>
  Customer Convenience Fee charged by the biller, in paise. Maps to the biller's `fee_config.fee_types[].fee_code=CCF1` returned by the Fetch Billers API and to NPCI's `custConvFee`. Pass `0` if the biller charges no CCF.
</ParamField>

<ParamField body="payments" type="array" required>
  Payment details collected via the Razorpay PG. Use multiple objects for split payments across methods.
</ParamField>

<ParamField body="payments[].id" type="string" required>
  Razorpay payment id received from the Razorpay PA post Payment Create. Required as per NPCI guidelines. For example, `pay_MbJ5AvwNpAkfLB`.
</ParamField>

<ParamField body="payments[].provider" type="string" required>
  Payment provider. Hardcode to `razorpay` when the payment is processed via the Razorpay gateway.
</ParamField>

<ParamField body="payments[].amount" type="integer" required>
  Amount paid via the selected method, in paise. Must match the payment amount captured through the PG.
</ParamField>

<ParamField body="payments[].currency" type="string" required>
  ISO 4217 currency code. Only `INR` is supported currently.
</ParamField>

<ParamField body="payments[].method" type="string" required>
  Payment method. Maps to NPCI `paymentMode`. Supported values: `card`, `upi`, `netbanking`, `wallet`.
</ParamField>

<ParamField body="payments[].card" type="object">
  Required when `method` is `card`.
</ParamField>

<ParamField body="payments[].card.type" type="string" required>
  Card type. Values: `credit`, `debit`, `prepaid`.
</ParamField>

<ParamField body="payments[].card.last4" type="string" required>
  Last 4 digits of the card.
</ParamField>

<ParamField body="payments[].card.auth_code" type="string" required>
  Hardcode to `Card` as per NPCI guidelines.
</ParamField>

<ParamField body="payments[].upi" type="object">
  Required when `method` is `upi`.
</ParamField>

<ParamField body="payments[].upi.vpa" type="string" required>
  Customer's actual UPI VPA if available; otherwise pass the Razorpay payment id.
</ParamField>

<ParamField body="payments[].netbanking" type="object">
  Required when `method` is `netbanking`.
</ParamField>

<ParamField body="payments[].netbanking.account_number" type="string" required>
  Customer's account number if available; otherwise hardcode to `PG`.
</ParamField>

<ParamField body="payments[].netbanking.ifsc_code" type="string" required>
  IFSC code if available; otherwise pass the Razorpay payment id.
</ParamField>

<ParamField body="payments[].wallet" type="object">
  Required when `method` is `wallet`.
</ParamField>

<ParamField body="payments[].wallet.name" type="string" required>
  Name of the actual wallet used. For example, `amazon_pay`, `payzapp`.
</ParamField>

<ParamField body="bills" type="array" required>
  Bills being paid. Minimum 1 bill required for Normal Pay. Pass an empty array `[]` for Direct Pay (no bill was retrieved).
</ParamField>

<ParamField body="bills[].bill_number" type="string">
  `bills[].bill_number` received in the Fetch Bill Request response. Required when a bill was retrieved.
</ParamField>

<ParamField body="bills[].amount" type="integer" required>
  Bill amount in paise. Must match the bill amount returned by the Fetch Bill Request API or satisfy the biller's `payment_config.amount_exactness` rules.
</ParamField>

<ParamField body="bills[].currency" type="string" required>
  Currency code. Hardcode to `INR`.
</ParamField>

<ParamField body="customer" type="object">
  Customer information passed as dynamic key-value pairs. Free-form metadata for your own tracking and reconciliation. Razorpay echoes these back on responses and webhooks without validation.
</ParamField>

<ParamField body="customer.id" type="string">
  Dynamic key-value pair holding your customer reference. Not validated by Razorpay.
</ParamField>

<ParamField body="customer.name" type="string">
  Dynamic key-value pair holding the customer name. Not validated by Razorpay.
</ParamField>

<ParamField body="customer.mobile" type="string" required>
  Customer's mobile number with country code. For example, `919000090000`.
</ParamField>

<ParamField body="customer.email" type="string">
  Dynamic key-value pair holding the customer email. Not validated by Razorpay.
</ParamField>

<ParamField body="biller_data" type="object">
  Bill account holder details. Mandatory if `bill_request_id` is not provided.
</ParamField>

<ParamField body="biller_data.account_holder" type="object">
  Dynamic key-value pairs that identify the customer's account at the biller. The keys and values are dictated by the biller's `account_holder_config` returned by the [Fetch Billers](/docs/api/bills/bill-payments/fetch-billers) API. Razorpay does not control the keys nor the values - pass them exactly as defined by the biller.
</ParamField>

<ParamField body="device" type="object">
  Info about the device initiating the bill payment. Mandatory if `bill_request_id` is not provided.
</ParamField>

<ParamField body="device.initiating_channel" type="enum" required>
  Channel from which the bill payment is initiated. Values:

  * `internet` - Web
  * `mobile` - Mobile App
  * `pos` - Point of Sale
  * `mpos` - Mobile Point of Sale
</ParamField>

<ParamField body="device.mobile" type="string">
  End agent's mobile number (10 digits) for offline channels. Required for `pos`, `mpos` channels.
</ParamField>

<ParamField body="device.geocode" type="string">
  Latitude, Longitude. Required for `internet`, `mobile`, `pos`, `mpos` channels. For example, `12.9667,77.5667`.
</ParamField>

<ParamField body="device.ip" type="string">
  IPv4 or IPv6 address. Required for `internet`, `mobile` channels.
</ParamField>

<ParamField body="device.mac" type="string">
  MAC address. Required for `internet` channels. For example, `00-0D-60-07-2A-F0`.
</ParamField>

<ParamField body="device.os" type="string">
  Operating system. Required for `mobile`, `mpos` channels. For example, `android`, `iOS`.
</ParamField>

<ParamField body="device.app" type="string">
  Application name. Required for `mobile`, `mpos` channels. For example, `amazon_pay`.
</ParamField>

<ParamField body="device.imei" type="string">
  Device IMEI number. Required for the `mobile` initiating channel.
</ParamField>

## Response Parameters

<ResponseField name="id" type="string">
  Razorpay's unique bill payment transaction id. Use this for internal tracking, refund reference, reconciliation and to poll for the final status. For example, `bill_pay_xxxx`.
</ResponseField>

<ResponseField name="entity" type="string">
  Entity type. Always `bill_payment.payment`.
</ResponseField>

<ResponseField name="status" type="string">
  Initial payment status. `processing` on the create response. Poll [Fetch Bill Payment](/docs/api/bills/bill-payments/fetch-bill-payment) for the final state. Possible final values: `success` (NPCI `responseCode=000`), `pending` (NPCI `responseCode=091`) or `failed` (any other NPCI `responseCode`).
</ResponseField>

<ResponseField name="customer" type="object">
  Customer who made the payment. Echoed from the request.
</ResponseField>

<ResponseField name="biller_id" type="string">
  Razorpay biller entity id. Use for internal biller identification, reporting and analytics.
</ResponseField>

<ResponseField name="gateway_biller_id" type="string">
  NPCI biller id.
</ResponseField>

<ResponseField name="gateway_transaction_id" type="string">
  BBPS/NPCI transaction reference number. Fixed length of 12 or 20 characters. Maps to NPCI `txnReferenceId`. Store for NPCI reconciliation, settlement tracking and dispute resolution.
</ResponseField>

<ResponseField name="gateway" type="string">
  Bill payment gateway used. Defaults to `bbps`.
</ResponseField>

<ResponseField name="created_at" type="integer">
  UNIX timestamp when the bill payment was created at Razorpay.
</ResponseField>

## Errors

<AccordionGroup>
  <Accordion title="The `ref_id` is required.">
    **Code:** `400`

    `ref_id` is required.

    **Solution:** Pass either a valid `bill_request_id` (Normal Pay) or the Direct Pay fields (`biller_id`, `biller_data.account_holder`, `device`).
  </Accordion>

  <Accordion title="The `bill_request_id` is invalid or expired.">
    **Code:** `400`

    Invalid or expired `ref_id`.

    **Solution:** Use a `bill_request_id` from a successful bill request that has not yet expired (`expiry_at`).
  </Accordion>

  <Accordion title="The `bill_pay_amount` does not match the fetched amount.">
    **Code:** `400`

    Payment amount does not match fetch/validate amount.

    **Solution:** Use the `bill.amount` returned in the successful bill request or pass a value that satisfies the biller's `amount_exactness` rules.
  </Accordion>

  <Accordion title="The customer parameters do not match the bill request.">
    **Code:** `400`

    Customer parameters do not match fetch/validate request.

    **Solution:** Pass the same customer parameters used during the bill request.
  </Accordion>

  <Accordion title="The payment method is not supported for this biller.">
    **Code:** `400`

    Payment method not supported for this biller.

    **Solution:** Check the biller's supported `payment_config.methods` and retry with a supported method.
  </Accordion>

  <Accordion title="The payment amount exceeds the maximum limit.">
    **Code:** `400`

    Payment amount exceeds maximum limit of `{max_amount}` for `{payment_mode}`.

    **Solution:** Pass an amount within the biller's `payment_config.methods[].max_limit` or split across methods.
  </Accordion>

  <Accordion title="Required payment instrument details are missing.">
    **Code:** `400`

    Required payment instrument details missing for `{payment_mode}`.

    **Solution:** Include the appropriate sub-object (`card`, `upi`, `netbanking`, `wallet`) with all mandatory fields.
  </Accordion>

  <Accordion title="Payment authorisation not found from PA.">
    **Code:** `400`

    Payment authorisation not found from PA.

    **Solution:** Ensure the Razorpay PG payment is in a `captured` state before calling this API.
  </Accordion>

  <Accordion title="The `X-Bill-Payments-Idempotency` header is missing or invalid.">
    **Code:** `400`

    The idempotency key must be 4-36 characters and contain only alphanumeric characters, hyphens, underscores or spaces.

    **Solution:** Provide a unique idempotency key. A UUID v4 is recommended.
  </Accordion>

  <Accordion title="Bill payment failed at biller.">
    **Code:** `502`

    The biller rejected the bill payment.

    **Solution:** Check `metadata.payment_id` and initiate a refund on the PG payment if money was collected.
  </Accordion>

  <Accordion title="Duplicate payment at biller.">
    **Code:** `502`

    Bill payment failed at biller because of a duplicate payment.

    **Solution:** Poll Fetch Bill Payment for the original payment status before retrying.
  </Accordion>

  <Accordion title="Payment request timed out at NPCI.">
    **Code:** `502`

    NPCI did not respond within the timeout window.

    **Solution:** Poll Fetch Bill Payment for the final state. Do not retry until status reaches `success` or `failed`.
  </Accordion>

  <Accordion title="Biller temporarily unavailable for payments.">
    **Code:** `502`

    The biller is temporarily unavailable for payments.

    **Solution:** Retry the request after a short interval.
  </Accordion>

  <Accordion
    title="The API `<key/secret>
` provided is invalid."
  >
    **Code:** `401`

    The API credentials passed in the request differ from the ones generated on the Dashboard.

    **Solution:** Ensure your API key and secret are correct and active.
  </Accordion>
</AccordionGroup>
