UPIstar API Reference
Everything you need to create UPI orders, receive real-time payment webhooks and confirm payments in your own application.
Introduction
The UPIstar API is a JSON REST API served over HTTPS. Every request and response body is JSON, every amount is in Indian Rupees (INR), and every timestamp is ISO 8601.
A typical integration has three steps:
- Create an order from your server when the customer clicks Pay.
- Redirect the customer to the returned
payment_url, or render theupi_intentyourself. - Receive a webhook when payment succeeds and mark the order as paid in your database.
Authentication
Authenticate each request with your secret API key, sent as a Bearer token. Find your keys in Dashboard → Settings → API Keys.
Authorization: Bearer usk_live_xxxxxxxxxxxxxxxxxxxxxxxxSandbox & live mode
Each account has two key pairs. Keys starting with usk_test_ create sandbox orders that never move real money. Use the Simulate payment button in the dashboard to trigger a test webhook. Switch to usk_live_ keys when you are ready to go live.
Create order
Creates a payment request and returns a hosted checkout URL with a dynamic QR code.
Body parameters
| Field | Type | Description |
|---|---|---|
| order_id | string | Required. Your unique order reference (max 40 chars). Re-sending the same ID returns the existing order. |
| amount | number | Required. Amount in INR, from 1 to 100000, up to 2 decimals. |
| customer_name | string | Customer's name, shown on the checkout page. |
| customer_email | string | Optional email for the payment receipt. |
| customer_mobile | string | Optional 10-digit mobile number. |
| redirect_url | string | Where to send the customer after payment. |
| note | string | Short description shown in the customer's UPI app. |
| meta | object | Up to 10 key/value pairs returned to you in webhooks. |
Example request
curl -X POST https://api.upistar.in/v1/orders \ -H "Authorization: Bearer $UPISTAR_KEY" \ -H "Content-Type: application/json" \ -d '{ "order_id": "INV-1042", "amount": 1499, "customer_name": "Priya Rao", "redirect_url": "https://yourshop.in/thanks" }'
const res = await fetch('https://api.upistar.in/v1/orders', { method: 'POST', headers: { Authorization: `Bearer ${process.env.UPISTAR_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ order_id: 'INV-1042', amount: 1499 }), }); const { data } = await res.json();
$ch = curl_init('https://api.upistar.in/v1/orders'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('UPISTAR_KEY'), 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode(['order_id' => 'INV-1042', 'amount' => 1499]), ]); $order = json_decode(curl_exec($ch), true);
import os, requests r = requests.post( "https://api.upistar.in/v1/orders", headers={"Authorization": f"Bearer {os.environ['UPISTAR_KEY']}"}, json={"order_id": "INV-1042", "amount": 1499}, timeout=15, ) order = r.json()["data"]
body := strings.NewReader(`{"order_id":"INV-1042","amount":1499}`) req, _ := http.NewRequest("POST", "https://api.upistar.in/v1/orders", body) req.Header.Set("Authorization", "Bearer "+os.Getenv("UPISTAR_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req)
HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://api.upistar.in/v1/orders")) .header("Authorization", "Bearer " + System.getenv("UPISTAR_KEY")) .header("Content-Type", "application/json") .POST(BodyPublishers.ofString("{\"order_id\":\"INV-1042\",\"amount\":1499}")) .build(); HttpResponse<String> res = HttpClient.newHttpClient().send(req, BodyHandlers.ofString());
Response 201 Created
{
"status": true,
"message": "Order created",
"data": {
"order_id": "INV-1042",
"amount": "1499.00",
"currency": "INR",
"status": "pending",
"payment_url": "https://pay.upistar.in/ord_8f2k1m9x4q",
"upi_intent": "upi://pay?pa=…&am=1499.00&tn=INV-1042",
"expires_at": "2026-11-06T10:47:18+05:30"
}
}Check order status
Returns the latest state of an order. Use it as a fallback when a webhook is delayed, or to confirm a payment before you ship.
{
"status": true,
"data": {
"order_id": "INV-1042",
"amount": "1499.00",
"status": "success",
"utr": "402188761290",
"payer_vpa": "priya@okbank",
"paid_at": "2026-11-06T10:42:18+05:30"
}
}Verify by UTR
If a customer paid but the order still shows pending (for example, they paid manually instead of through the QR), submit the 12-digit UTR from their UPI app. UPIstar checks it against the received credit and confirms the order if the amount matches.
{ "utr": "402188761290" }Webhooks
Set your webhook URL in Dashboard → Settings → Webhooks. UPIstar sends a POST request with a JSON body whenever an order changes state.
| Event | When it fires |
|---|---|
| payment.success | Payment was received and verified. |
| payment.failed | The customer's UPI app reported a failure. |
| order.expired | The order expired without payment. |
Payload
{
"event": "payment.success",
"order_id": "INV-1042",
"amount": "1499.00",
"currency": "INR",
"status": "success",
"utr": "402188761290",
"payer_vpa": "priya@okbank",
"meta": { "cart_id": "c_7781" },
"paid_at": "2026-11-06T10:42:18+05:30"
}Retries
Respond with any 2xx status within 10 seconds. Otherwise UPIstar retries up to 6 times over 24 hours (after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h). Every attempt is visible in the webhook log, where you can also replay it manually.
200.Verify signature
Each webhook includes an X-UPIstar-Signature header: an HMAC-SHA256 of the raw request body, signed with your webhook secret. Always verify it before trusting the payload.
import crypto from 'node:crypto'; function isValid(rawBody, header, secret) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); return header?.length === expected.length && crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected)); }
$raw = file_get_contents('php://input'); $expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('UPISTAR_WEBHOOK_SECRET')); $given = $_SERVER['HTTP_X_UPISTAR_SIGNATURE'] ?? ''; if (!hash_equals($expected, $given)) { http_response_code(401); exit; }
import hmac, hashlib def is_valid(raw_body: bytes, header: str, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header or "")
Order statuses
| Status | Meaning |
|---|---|
| pending | Order created, waiting for the customer to pay. |
| success | Payment received and verified. Safe to fulfil. |
| failed | Payment attempt failed or was declined. |
| expired | No payment received before the order expired. |
Errors
Errors return "status": false with a human-readable message and a machine-readable code.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A required field is missing or malformed. |
| 401 | unauthorized | API key is missing, invalid or revoked. |
| 404 | not_found | No order exists with that ID. |
| 409 | duplicate_order | The order ID exists with a different amount. |
| 422 | utr_mismatch | The UTR doesn't match a credit for this order. |
| 429 | rate_limited | Too many requests. Retry after the Retry-After header. |
| 500 | server_error | Something went wrong on our side. Safe to retry. |
Plugins & SDKs
WooCommerce / WordPress
Download the plugin from your dashboard, upload it under Plugins → Add New, paste your API key and enable UPIstar as a payment method. Orders auto-complete when paid.
Sample code
Ready-to-run examples for PHP, Node.js, Python, Go and Java are available in your dashboard under Developers → Samples.
Need help integrating? Email support@upistar.in or call 1800 909 111.