RobinPay

Payments API

Accept UPI payments through RobinPay's payments platform. Create an order, hand your customer the payment link, and get notified the moment it's paid.

Introduction

All API requests are made to the gateway over HTTPS. The base URL is:

https://api.robinpay.in

Every endpoint is versioned under /v1. Requests and responses are JSON. Monetary amounts are always integers in paise (₹1 = 100 paise) to avoid floating-point rounding.

Successful responses are wrapped as { "success": true, "data": { … } }; errors as { "success": false, "error": { "code", "message" } }.

Authentication

Authenticate server-to-server calls with an API key, issued from the merchant portal under API keys, sent as a bearer token:

Authorization: Bearer rb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Or, equivalently, the X-RB-API-Key header. Keep the key secret and server-side — never ship it in client code. Rotate or revoke it any time from the portal.

The key prefix selects the environment. rb_live_… keys run in live mode (real orders, your live wallet); rb_test_… keys run in sandbox mode (test orders, a pre-funded test wallet). The key alone decides the mode — the request body and responses are identical. Live and sandbox data never mix. See Sandbox / test mode.

How payments work

RobinPay is your payment provider. You integrate with RobinPay only — create an order and RobinPay provides the UPI payment rail your customer pays on. There are no third-party accounts, credentials, or banks for you to set up or manage; RobinPay runs the entire payment infrastructure for your account.

Your account is enabled for payments as part of onboarding. If it isn't active yet when you create an order, the API returns NO_PROVIDER_CONFIGURED — contact your account manager.

Payment outcomes

When a customer pays — or the payment link expires — RobinPay reconciles the order automatically. There's nothing for you to set up; you read the result by polling Retrieve an order (the order's status reflects the latest outcome).

How an outcome maps to order status

Payment resultOrder becomesEffect
PaidsuccessOrder marked paid; commission captured
Link expired unpaidexpiredCommission released
Failed / cancelledfailedCommission released
Reconciliation is idempotent — a repeated or late payment notification for an order already in a final state is ignored, so the status you read never flip-flops.

Sandbox / test mode

Sandbox lets you build and test the full integration without moving real money. It's the same API and the same flow — only the API key differs — so code written against sandbox works unchanged in live.

1. Generate a sandbox API key

In the merchant portal → API keys, choose Sandbox when issuing a key. Sandbox keys are prefixed rb_test_ (live keys are rb_live_). Use the sandbox key as the bearer token exactly like a live key.

2. Create test orders

Call POST /v1/orders with your sandbox key — same body as live. Test orders carry a separate environment (test) and draw on your auto-funded sandbox wallet so commission checks never block testing. They're visible in the merchant portal alongside live — switch the Live / Sandbox toggle on the Orders page to view them, and the dashboard shows both your live and sandbox balances.

Forcing an outcome (sandbox). The paise ending of amount drives the test result (amount is always an integer in paise — ₹1 = 100 paise):
  • ends in .00 → paid success — e.g. amount: 10000 (₹100.00)
  • ends in .51failed — e.g. amount: 10051 (₹100.51)
i.e. the last two digits of the paise amount are the trigger (…00 succeeds, …51 fails). The outcome is reconciled automatically, exactly like live.
Live and sandbox never mix: a rb_test_ key only ever sees test data, and rb_live_ only live. The order lifecycle, retrieve, and error shapes are identical in both.

Create an order

POST /v1/orders

Creates a payment order and returns a paymentLink — a RobinPay-hosted payment page for your customer to pay on. RobinPay prepares the payment first, so a successful response always carries a ready-to-use link.

Body parameters

FieldTypeDescription
amountintegerrequiredAmount in paise. Min 100 (₹1).
currencystringrequiredINR
methodstringrequiredOne of upi, card, netbanking, wallet.
deliveryTypestringrequiredlink (hosted page). UPI intent / QR (qr) is temporarily unavailable.
merchantOrderIdstringrequiredYour own order id. 1–64 chars: letters, digits, _, -. Unique per merchant.
idempotencyKeystringrequired8–128 chars. A retry with the same key + body never creates a duplicate — it returns the existing orderId with idempotentReplay: true and a pointer to GET /v1/orders/{orderId} (the create response is a snapshot, so fetch the order for its current status).
expiresInMinutesintegeroptionalMinutes until the link expires. Min 5; defaults to 5 if omitted.
descriptionstringoptionalFree-text note, ≤ 200 chars.

Example request

curl -sS -X POST https://api.robinpay.in/v1/orders \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer rb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -d '{
    "amount": 10000,
    "currency": "INR",
    "method": "upi",
    "deliveryType": "link",
    "merchantOrderId": "order-1001",
    "idempotencyKey": "order-1001-9f2a7c",
    "expiresInMinutes": 10
  }'

Example response

{
  "success": true,
  "data": {
    "orderId": "ORD_01KV8AZGJ1PFY5NJ2P1PB2YH1Y",
    "merchantOrderId": "order-1001",
    "status": "pending",
    "amount": "10000",
    "currency": "INR",
    "method": "upi",
    "deliveryType": "link",
    "paymentLink": "https://api.robinpay.in/pay/ORD_01KV8AZGJ1PFY5NJ2P1PB2YH1Y",
    "expiresAt": "2026-06-16T13:57:20.000Z"
  }
}

Hand paymentLink to your customer — it opens RobinPay's hosted payment page. The order starts pending; you'll learn the outcome by polling retrieve once RobinPay reconciles the payment.

Retrieve an order

GET /v1/orders/{orderId}

Fetch the current state of an order by its RobinPay orderId (the ORD_… value from create). status here is the live value — poll this (rather than re-reading the create response, which is a point-in-time snapshot) to learn the outcome.

Scoped to the key's mode: a rb_live_ key only resolves live orders, a rb_test_ key only test orders — fetching an order from the other mode returns 404 ORDER_NOT_FOUND.
curl -sS https://api.robinpay.in/v1/orders/ORD_01KV8AZGJ1PFY5NJ2P1PB2YH1Y \
  -H 'Authorization: Bearer rb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
{
  "success": true,
  "data": {
    "orderId": "ORD_01KV8AZGJ1PFY5NJ2P1PB2YH1Y",
    "merchantOrderId": "order-1001",
    "status": "success",
    "amount": "10000",
    "currency": "INR",
    "paidAt": "2026-06-16T13:50:11.000Z",
    "expiresAt": "2026-06-16T13:57:20.000Z",
    "createdAt": "2026-06-16T13:47:20.000Z"
  }
}

Order lifecycle

An order moves through these states:

StatusMeaning
pendingCreated; awaiting payment. Has a live payment link.
successPayment confirmed (paidAt set). Terminal.
failedPayment failed. Terminal.
expiredThe link expired before payment. Terminal.
Expiry: every order's link expires after expiresInMinutes (default 5). Once expired, the order moves to expired and the link can no longer be paid.

Errors

Errors use a stable code you can branch on plus a human-readable message:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient wallet balance to cover the commission on this order."
  }
}
CodeWhen
AUTH_MISSING_TOKEN / AUTH_INVALID_TOKENMissing or invalid API key.
NO_PROVIDER_CONFIGUREDYour account isn't enabled for payments yet for the requested method — contact your account manager.
DUPLICATE_MERCHANT_ORDER_IDmerchantOrderId already used for your account.
INSUFFICIENT_BALANCEPrepaid wallet can't cover the order's commission.
ALL_PROVIDERS_FAILEDRobinPay couldn't create the payment right now — safe to retry.
ORDER_NOT_FOUNDNo order with that id for your account.

Validation errors (HTTP 400) name the offending field and the allowed format in the message — useful while integrating.