Developers

API Reference

Endpoints, request fields, response shapes and error codes.

Last updated: 31 October 2026

Base URL and authentication

All endpoints are under https://cashvya.com and return JSON. Amounts are accepted in rupees and returned both in rupees (amount) and paise (amountPaise), so you never have to guess the unit.

Server-to-server

Send your secret key either way:

Authorization: Bearer cashvya_live_YOUR_KEY
X-Api-Key: cashvya_live_YOUR_KEY

Browser

A browser cannot hold a secret. The SDK identifies you with your public merchant id in X-Merchant-Id. A browser-created payment is less trusted: redirect URLs are restricted to origins you own, and you can switch browser-created payments off entirely in your dashboard, which makes POST /api/v1/payments return 403 BROWSER_CHECKOUT_DISABLED for anything but your own server.

A wrong key fails loudly

If you send a cashvya_live_ key that has been regenerated or revoked, the request returns 401 INVALID_API_KEY. It does not silently fall back to an untrusted call.

POST /api/v1/payments

Creates a payment session and returns the checkout URL.

Request

FieldTypeRequiredNotes
orderIdstringYesYour reference. Up to 128 characters, no control characters. Unique per order.
amountstring / numberYesRupees, max 2 decimals, e.g. "499.00"
currencystringNoDefaults to INR
methodstringNoDefaults to UPI. Also CASH_COUNTER, CARD_COUNTER for in-person.
customerobjectNofullName, email, phoneNumber. Shown to you on the payment and in the webhook.
returnUrlstringNoWhere to send the customer after success. Falls back to your dashboard setting.
cancelUrlstringNoWhere to send the customer if they cancel.
metadataobjectNoReturned to you unchanged in the webhook. Gateway-reserved keys are stripped.
idempotencyKeystringNoOr send the Idempotency-Key header.

Responses

201 Created — a new payment:

{
  "paymentId": "7f3c1e62-...",
  "orderId": "ORDER-1042",
  "amount": "499.00",
  "amountPaise": "49900",
  "currency": "INR",
  "status": "INITIATED",
  "queuePosition": 0,
  "checkoutUrl": "https://cashvya.com/checkout/7f3c1e62-..."
}

200 OK — the same orderId already exists. This is what makes retries safe:

  • Already paid: the original payment is returned with "status": "SUCCESS". Do not charge again.
  • Still open: the existing payment is returned with "activeSessionDetected": true, so you can resume the same checkout instead of creating a second one.
  • Previously failed or expired: a fresh payment is created for the same orderId.

Errors

StatusCodeMeaning
400—orderId or amount missing, or the amount is not a valid rupee value
400INVALID_ORDER_IDOver 128 characters or contains control characters
401INVALID_API_KEYKey revoked, regenerated or mistyped
401INVALID_MERCHANT_KEYX-Merchant-Key did not match your webhook secret
403MERCHANT_SUSPENDEDYour account is suspended and cannot take payments
403BROWSER_CHECKOUT_DISABLEDYou require server-created payments; create it with your secret key
409DEVICE_SESSION_MISMATCHThat payment was started on a different device
503UPI_UNAVAILABLENo receiving UPI account is available right now. Retry shortly.

GET /api/v1/payments/:paymentId

Reads a payment. The response depends on whether you prove ownership.

Without ownership

Anyone holding the payment id gets the public view only:

{
  "paymentId": "7f3c1e62-...",
  "merchantId": "00000000-...",
  "pageSlug": null,
  "orderId": "ORDER-1042",
  "amount": "499.00",
  "amountPaise": "49900",
  "currency": "INR",
  "status": "SUCCESS",
  "method": "UPI",
  "completedAt": "2026-10-31T06:12:40.112Z"
}

With ownership

Send X-Merchant-Key: <your whsec_… webhook secret>, or your admin credential, to additionally receive:

{
  "internalStatus": "SUCCESS",
  "verificationLevel": 2,
  "verificationSources": ["SMS", "GMAIL"],
  "verificationTag": "BANK_VERIFIED",
  "returnUrl": "https://yourshop.com/thank-you",
  "metadata": { "...": "your metadata, plus the customer summary" }
}

404 if the payment id does not exist.

GET /api/v1/checkout/:paymentId/state

The live state the checkout page itself polls. Useful if you build your own payment screen. Includes status, timeRemainingSeconds, expiresAt, serverNow, upiUri, payeeVpa, payeeName, enabledMethods, providers and the merchant's public branding.

A terminal payment returns "isTerminal": true with timeRemainingSeconds: 0.

Prefer the webhook

This endpoint exists to render a screen. For fulfilment, use the webhook or GET /api/v1/payments/:id with your key.

Payment statuses

StatusTerminalMeaning
INITIATEDNoCreated. The customer has not started paying, and no timer is running yet.
QUEUEDNoWaiting for a receiving UPI slot to free up.
ACTIVENoHolds a slot. The customer has about 5 minutes to pay.
PROCESSINGNoThe customer says they paid; Cashvya is matching it against bank notifications.
SUCCESSYesA bank credit was matched to this payment. Safe to fulfil.
FAILEDYesNo matching credit was found within the verification window.
EXPIREDYesThe payment window elapsed without payment.
CANCELLEDYesThe customer abandoned the checkout.
BLOCKEDYesStopped by fraud controls.

Webhooks are sent for the terminal outcomes only.

Key management

These are dashboard endpoints, authenticated with your dashboard session.

Method and pathDoes
GET /api/merchant/keysMasked status of your key and webhook secret
POST /api/merchant/keysGenerate a key. Returns it once and revokes the previous one.
DELETE /api/merchant/keysRevoke the key. Integrations using it stop immediately.
POST /api/merchant/webhook-secretRotate the webhook signing secret
POST /api/merchant/keys/revealShow a stored secret again. Requires { password, what }.
Rotating is a breaking change

Generating a new key invalidates the old one at once, and rotating the webhook secret means deliveries are signed with the new secret from that moment. Deploy the new value first.

Error format

Errors are JSON with a human message and, where it helps you branch, a stable code:

{ "error": "This merchant account is suspended and cannot accept payments.",
  "code": "MERCHANT_SUSPENDED" }

Branch on code, never on the text of error.