Skip to main content
This guide shows you how to connect your AI agent to the AgentPay MCP (Model Context Protocol) server and pay a customer’s bill from their Cashfree AgentPay wallet. It also covers testing in sandbox and going live.
Before you start, make sure you have:
  • AgentPay access and a client API key for sandbox (for testing) or production (for live payments). To get both, contact the Cashfree sales team. They set up AgentPay for you and share your API keys. You don’t need a Cashfree merchant account.
  • An AI agent or MCP client that supports the Streamable HTTP transport and custom request headers, such as Claude Code, Cursor, or a custom agent built with an MCP SDK.

Connect your agent

Sandbox and production work the same way. Only the server URL and your API key change:
Always pair the sandbox key with the sandbox URL, and the production key with the production URL. A mismatch fails with HTTP 401.
Choose your client and add the server. The examples use the sandbox URL:
Run this command in your terminal:
Add -s user to make AgentPay available in all your projects. Run claude mcp list to check it. The output lists agentpay as connected.

Supported bill categories

The wallet can pay most bill categories, including Electricity, Water, Gas, LPG, Mobile Postpaid, Broadband, Landline, DTH, Cable TV, Insurance, Fastag, Education, Municipal Taxes, Rental, Subscription, Hospital, Donation and Clubs.
The wallet can’t pay Credit Card or Loan Repayment bills. Don’t offer billers in these categories. If a customer asks, tell them to pay that bill another way.
Some billers also don’t accept wallet payments. wallet_pay refuses these with wallet_not_supported_by_biller.

Choose a payment flow

Each biller in list_billers tells you whether your agent fetches the bill, validates the customer’s details, or pays directly. Check fetch_requirement and support_bill_validation, then follow the matching flow:

Pay a bill, step by step

Here is the whole journey at a glance for a biller whose bill your agent fetches. The consent step happens only the first time a customer pays: The following steps continue the BESCOM example from the AgentPay MCP overview and show the tool calls your agent makes at each stage.
1

Find the biller

Identify which biller the customer means. Use list_biller_categories to map words like “electricity” to a category, then search for the biller by name with list_billers:
Arguments
The response gives you the biller_id, the details the customer must provide (in biller_customer_params), and which flow to follow (fetch_requirement and support_bill_validation). Here, BESCOM needs a Customer ID and the bill must be fetched.
Always pass biller_name. Some categories have more than 600 billers. If several billers match, ask the customer which one is theirs rather than guessing.
2

Fetch the bill

Ask the customer only for the details the biller needs, plus their mobile number. Then request the bill with request_bill_fetch:
Arguments
You get back a ref_id. The bill takes a moment to arrive, so call poll_bill_fetch with the ref_id every 2 to 3 seconds until response_code is 000. Both the status and the bill are inside data, in bill_fetch_response and biller_response:
Response (excerpt)
Save the ref_id. A ref_id is valid for 15 minutes, so if more time passes before you debit the wallet, fetch the bill again.
3

Confirm with the customer

Show the customer the name, amount and due date, and wait for a clear “yes, pay it”. No money moves until they agree. Do this for every bill, even after the customer has approved your agent.
4

Get the customer's consent (first time only)

If you don’t yet hold a token for this customer, call create_mandate_session with their mobile number. You get a consent_link to send them, ideally in the same message as the bill.The customer opens the link, logs in with a one-time password (OTP), and approves a mandate for your agent. Meanwhile, call get_mandate_session with the session_id until the status is CONFIRMED. That response contains:
  • An access_token, which lets you use the customer’s wallet. It lasts about an hour.
  • A refresh_token, which gets you a new access_token later without asking the customer again.
  • The terms the customer agreed to, such as max_amount_per_month and max_debits_per_month.
Store the tokens and terms securely on your side, against this customer.
In sandbox, the OTP on the consent page is always 111000.
5

Check the wallet balance

Call get_wallet_balance with the access_token. Go ahead only when eligibility is true, blocked is false, wallet_exists is true, and balance covers the bill.If the balance doesn’t cover the bill, call topup_wallet to create a top-up and send the customer the payment_link. Call get_topup_status until the order is PAID, then check the balance again.
6

Debit the wallet

Call wallet_pay to take the money from the wallet. Use a new, unique order_id and pass the bill’s ref_id as reference_id:
Arguments
The response says ACCEPTED, which isn’t the final result yet. Call get_pay_status with the same order_id until txstatus is SUCCESS, and save the transaction_id it returns.
7

Pay the biller

Now tell the biller the bill is paid. Call request_bill_payment with the transaction_id from step 6 and the ref_id from step 2:
Arguments
You don’t send the bill details. The server reads the customer details, bill and amount back from the fetch, so they always match what the biller sent.You get back a transaction_ref_id. Call poll_bill_payment with it and the ref_id until the status is SUCCESS or FAILURE.
8

Share the receipt

On SUCCESS, tell the customer the bill is paid and give them the utr (unique transaction reference). This is their proof of payment.If the status is FAILURE, the wallet has already been debited, so don’t charge it again. Refund the customer with refund_payment instead (see Refunds).

Flow variations

The step-by-step flow covers a first-time customer paying a fetched bill. The following variations change only some of the steps.

Returning customers

Once a customer has approved your agent and you hold their tokens, skip the consent step entirely:
If the access_token has expired, call refresh_access_token with the refresh_token first. Send a new consent link only if the refresh fails, which happens when the customer’s consent expires or is revoked.

Validate-first billers

Some billers can’t return a bill, but they can check that the customer’s details are valid. For these billers, the flow changes in the following places:
  1. Call request_bill_fetch as usual. Cashfree validates the customer’s details instead of fetching a bill, and the response has flow set to VALIDATE_AND_PAY.
  2. Poll with poll_bill_fetch until response_code is 000. The response confirms the details are valid but has no biller_response, so there’s no bill amount.
  3. Ask the customer how much to pay, and read it back to confirm.
  4. In wallet_pay, pass the ref_id as reference_id and the customer’s amount as order_amount.
  5. In request_bill_payment, pass bill_fetch_ref_id as usual, plus customer_params (the same param_name and param_value pairs you sent to request_bill_fetch) and amount (the same amount as the debit).
The rest of the flow doesn’t change.

Direct payments such as donations

Some billers have no bill to fetch or validate, and donations are the most common example. The customer chooses the amount. For these billers, the flow changes in the following places:
  1. Skip request_bill_fetch and poll_bill_fetch. Ask the customer for the details in biller_customer_params and the amount, and read them back to confirm.
  2. In wallet_pay, send biller_id instead of reference_id.
  3. In request_bill_payment, leave out bill_fetch_ref_id. Send customer_params (the biller’s biller_customer_params, as param_name and param_value pairs) and amount (the same amount as the debit).
  4. In poll_bill_payment, leave out bill_fetch_ref_id.
The amount must stay within the biller’s limits for wallet payments.

Handle failures

When a tool fails, the result has isError: true and a message that explains what went wrong, for example Cashfree API error 400 for POST /pg/agentpay/wallet/pay: {...}. For the full result format, see Technical details. Return that message to your agent unchanged so it can recover. Tell the customer only that the agent couldn’t fetch or pay the bill, and offer to try again.

Recover without charging twice

The most important question after a failure is whether money has already left the wallet. Use this table to decide what to do:

Refunds

If a bill payment fails after the wallet debit succeeds, return the money to the wallet:
  1. Call refund_payment with the order_id of the debit, a unique refund_id you create, and the refund_amount.
  2. Call get_refund_status until refund_status changes from PENDING to SUCCESS.
When refund_status is SUCCESS, the money is back in the wallet. You can refund a debit whose txstatus is SUCCESS in either of these cases:
  • The bill payment failed.
  • The bill payment never reached the biller, and the debit is at least 15 minutes old. This covers a ref_id that expired between the debit and the bill payment.
You can’t refund a bill that was paid, or a debit whose bill payment is still in progress. You can’t refund more than you debited. Refunds still work after the customer’s consent expires or is revoked, and the access_token is optional for them.

Troubleshooting

Expand a problem to see the likely cause and fix.

Connection issues

The Authorization: Bearer header is missing or malformed, so the Cashfree gateway rejects the request before it reaches the server. Add the header to your MCP client configuration.
Your API key is wrong or revoked, or you used a sandbox key with the production URL (or the other way round). Make sure the key matches the server URL. If it still fails, raise a support request through the Merchant Support form.
The URL is wrong, or your client has cached an old configuration. Check the URL and reconnect. In Claude Code, use /mcp.

Payment issues

You didn’t pass biller_name. Add it to narrow the search.
A ref_id is valid for 15 minutes. Call request_bill_fetch again to get a fresh one.
This is a direct-payment biller, such as a donation. Skip the fetch and follow Direct payments such as donations.
The access_token is missing, wrong or expired. Call refresh_access_token. If that fails, send the customer a new consent link.
Call topup_wallet, send the customer the payment_link, and wait until get_topup_status shows PAID.
Read the reason field in the get_wallet_balance response. Usually, the fix is to start a new consent session.
The customer has reached their monthly limit and no money moved. Tell them, and suggest they pay another way or wait until next month.
You sent biller_id for a biller that supports bill fetch or validation. Call request_bill_fetch and pay with its ref_id as reference_id.
The amount breaks the biller’s rules. For a fetched bill, it must follow the biller’s payment_amount_exactness, for example the exact bill amount. Every amount must also stay within the biller’s limits for wallet payments and have at most 2 decimal places.
This biller doesn’t accept wallet payments. Tell the customer to pay this bill another way.
You reused an order_id for a different payment. Check get_pay_status for the first order before you try again with a new order_id. Every order_id must be unique across all your customers.
The bill was validated rather than fetched, or it’s a direct payment. Send customer_params and amount, as described in Flow variations.
bill_already_paid means the biller received the payment, so you can’t refund it. bill_payment_not_terminal means the bill payment is still in progress. Poll poll_bill_payment until it’s final, then refund only if it failed.
You treated rupees as paise. All amounts in AgentPay are in rupees.
A JSON parser rounded a long number. Keep all IDs as strings from start to finish.

Security

Two kinds of credentials protect AgentPay: your API key, which identifies your business, and the customer tokens, which let your agent pay from a customer’s wallet.

Protect your API key

Your MCP client sends your API key as a Bearer token on every request. The server forwards it to Cashfree and never stores it. It handles each request separately, so you see only your own customers and payments. The server masks the key as *** in any error message. Keep the key in your MCP client configuration or an environment variable, never in prompts or chat messages.

Protect customer tokens

The tokens from the consent step let your agent pay on the customer’s behalf. Follow these practices to keep them safe:
  • Never ask the customer for a token. Customers only know their bill and mobile number. Your agent gets tokens from the consent flow, not from the customer.
  • Store tokens securely on your side, encrypted and linked to the customer. The MCP server doesn’t keep them, so you pass the access_token on every wallet call.
  • Reuse them. The mandate is a standing permission, so don’t send a new consent link for every bill.
  • Stay within the limits. Track how much you’ve debited this month. Cashfree refuses debits that go over max_amount_per_month or max_debits_per_month.
  • Refresh when expired. Use refresh_access_token. Calling get_mandate_session again doesn’t extend the token.
  • Revoke when the customer asks. If the customer wants your agent to stop paying from their wallet, call revoke_consent. Every token for that consent stops working. Payments already in progress still complete, and refunds remain possible.

Test in sandbox

Before you go live, run the full flow in sandbox. Use these test values: To test, complete the following checks:
  1. First-time payment. Follow every step in Pay a bill, step by step. It passes when poll_bill_payment returns SUCCESS with a utr.
  2. Returning customer. Pay a second bill with the same access_token, without a new consent link.
  3. Direct payment. Pay a Donation biller without fetching a bill.
  4. Validate-first biller. If you plan to support these billers, pay one with an amount the customer gives.
  5. Revoke consent. Call revoke_consent, then check that wallet tools reject the old access_token.
  6. Error handling. Check that your agent responds sensibly to each of the following cases:
    • A missing Authorization header returns HTTP 401 with "type": "authentication_error".
    • Reusing an order_id for a different payment returns order_id_conflict.
    • Going over max_debits_per_month returns monthly_debit_limit_exceeded.
    • Paying a Credit Card or Loan Repayment bill is refused.
    • A wrong access_token returns 401 or 403.

Go live

When every sandbox check passes, complete the following steps to go live:
  1. Replace your sandbox API key with your production API key.
  2. Change the server URL to https://mcp.cashfree.com/agentpay/mcp.
  3. Remove any sandbox-only values from your agent, such as the test OTP 111000. In production, customers receive a real OTP on their mobile number.
  4. Pay one small real bill end to end and check that the customer gets a utr.
Nothing else changes between sandbox and production.

Get help

Raise a support request through the Merchant Support form. Include the order_id, transaction_id, transaction_ref_id and the time of the payment you’re asking about.