Auto Upi API documentation
Auto Upi is a self-serve UPI collection gateway. Create an order from your server and open the hosted checkout as a popup on your own website. Orders are confirmed automatically — no commission, money straight into your UPI account.
https://autoupi.in/api/public/v1application/jsonX-API-KeyQuickstart
- Create an account and sign in to the merchant console.
- Connect an account — pick PhonePe or Paytm and complete the setup on the Connect Accounts page.
- Generate an API key on the API Keys page. Copy it once; regenerating replaces the old key.
- Copy the webhook secret from API Keys and store both credentials securely on your server.
- Set your webhook URL on Config, or pass your HTTPS webhook endpoint when creating each order.
- Load the popup script once from
https://autoupi.in/checkout.js. - Create an order from your server, save its ID against the buyer, then call
AutoUpi.open({ url: payment_url })on your page. See Popup checkout. - Confirm payment through your signed webhook or a server-side status check, then update the same page and fulfil the order once.
Authentication
Every request carries your secret key in the X-API-Key header. Keys start withaupi_live_ and are stored hashed — an old key can never be shown again. Call the API only from your server; never expose the key in browser code.
X-API-Key: aupi_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/jsonCreate order
POST /api/public/v1/create-order
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Order amount in INR, 1 to 10,00,000. |
customer_name | string | No | Shown on the checkout page. |
webhook_url | string | No | HTTPS URL that receives the signed payment.paid webhook. |
link_type | string | No | one_time (default) or reusable. |
curl -X POST https://autoupi.in/api/public/v1/create-order \
-H "X-API-Key: aupi_live_your_key" \
-H "Content-Type: application/json" \
-d '{"amount": 1499, "customer_name": "Rahul Sharma", "webhook_url": "https://yoursite.com/autoupi-webhook.php"}'{
"ok": true,
"order_id": "ORD20260915A1B2C3",
"slug": "9fkq2wra",
"amount": 1499,
"payable_amount": 1499.37,
"status": "active",
"payment_url": "https://autoupi.in/pay/9fkq2wra"
}Always charge payable_amount: it can differ from amount by up to ₹0.99 so each active order stays unique. The customer pays payable_amount; your books are credited with the base amount.
Order status
GET /api/public/v1/order-status?order_id=ORD…
Check this endpoint from your server during checkout or after a popup callback to confirm payment before fulfilment. Your page calls your own status endpoint; it never sends the API key to Auto Upi directly.
curl "https://autoupi.in/api/public/v1/order-status?order_id=ORD20260915A1B2C3" \
-H "X-API-Key: aupi_live_your_key"{
"ok": true,
"order": {
"order_id": "ORD20260915A1B2C3",
"slug": "9fkq2wra",
"customer_name": "Rahul Sharma",
"amount": 1499,
"payable_amount": 1499.37,
"status": "paid",
"payer_name": "RAHUL SHARMA",
"paid_at": "2026-09-15T10:21:44.120Z",
"detected_at": "2026-09-15T10:21:52.480Z",
"expires_at": "2026-09-15T10:26:00.000Z"
}
}| Status | Meaning |
|---|---|
active | Waiting for payment. Links expire 5 minutes after creation. |
paid | A matching credit alert was detected. Safe to fulfil. |
expired | No payment detected in time. Create a new order. |
Webhooks
Each API key ships with a webhook secret (whsec_…) shown on the API Keys page. Pass awebhook_url when creating an order and we POST a signed payment.paid event the moment the order is confirmed as paid. You can also save one default webhook URL on the Config page in your dashboard — it is used for every order of your account when the create order call does not send its own webhook_url.
POST https://yoursite.com/autoupi-webhook.php
X-AutoUpi-Signature: t=1789453200,v1=6f3c…9ab
{
"event": "payment.paid",
"order_id": "ORD20260915A1B2C3",
"amount": 1499,
"payable_amount": 1499.37,
"status": "paid",
"payer_name": "RAHUL SHARMA",
"paid_at": "2026-09-15T10:21:44.120Z"
}
signed_payload = "{t}." + raw_request_body
v1 = hex(hmac_sha256(webhook_secret, signed_payload))<?php
// autoupi-webhook.php
$secret = getenv("AUTOUPI_WEBHOOK_SECRET");
$raw = file_get_contents("php://input");
$header = $_SERVER["HTTP_X_AUTOUPI_SIGNATURE"] ?? "";
parse_str(str_replace(",", "&", $header), $parts); // t=..., v1=...
$expected = hash_hmac("sha256", $parts["t"] . "." . $raw, $secret);
if (!hash_equals($expected, $parts["v1"] ?? "")) {
http_response_code(401);
exit("bad signature");
}
$event = json_decode($raw, true);
if ($event["event"] === "payment.paid") {
// idempotent: skip if this order_id is already fulfilled
mark_order_paid($event["order_id"], $event["amount"]);
}
http_response_code(200);
echo "ok";Always treat delivery as at-least-once and keep your handler idempotent.
Popup checkout
Keep the customer on your own website. Load our script once, create the order on your server as usual, then open the returned payment_url as a popup. The same Auto Upi payment page opens on top of your page and closes by itself after the result.
<script src="https://autoupi.in/checkout.js"></script>
<button id="pay">Pay now</button>
<p id="payment-status" role="status"></p>
<script>
const payButton = document.getElementById("pay");
const paymentStatus = document.getElementById("payment-status");
document.getElementById("pay").onclick = async () => {
payButton.disabled = true;
paymentStatus.textContent = "Starting payment…";
try {
// Your server uses the saved cart price, not a browser-supplied amount.
// Protect this request with your app's session and CSRF controls.
const response = await fetch("/checkout.php", { method: "POST" });
const order = await response.json();
if (!response.ok || !order.ok || !order.payment_url) {
throw new Error("Could not start payment. Please try again.");
}
if (!window.AutoUpi) throw new Error("Checkout could not load. Please try again.");
AutoUpi.open({
url: order.payment_url,
onSuccess: (result) => {
paymentStatus.textContent = "Confirming payment…";
// Call YOUR server's buyer-scoped order-status endpoint here.
// Show success only after it reads paid from Auto Upi.
// The signed webhook can also confirm the order independently.
payButton.disabled = false;
},
onFailure: (result) => {
paymentStatus.textContent = "Payment failed or expired. Please try again.";
payButton.disabled = false;
},
});
paymentStatus.textContent = "Waiting for payment…";
} catch (error) {
paymentStatus.textContent = error.message || "Could not start payment.";
payButton.disabled = false;
}
};
</script><?php
$ch = curl_init("https://autoupi.in/api/public/v1/create-order");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json", "X-API-Key: " . getenv("AUTOUPI_KEY")],
// Example price only: use your server's saved cart amount in production.
CURLOPT_POSTFIELDS => json_encode(["amount" => 100, "webhook_url" => "https://yoursite.com/autoupi-webhook.php"]),
]);
// For complete order saving and error handling, see checkout.php below.
header("Content-Type: application/json");
echo curl_exec($ch);The popup result only updates your screen. Deliver the product only after the signed webhook (or the order status API) says the order is paid. Update the current merchant page after confirmation; do not navigate the customer away. The snippets show integration hooks, not a ready-made store: connect them to your own saved orders, buyer authentication and fulfilment.
Popup size and setup checklist
- Desktop: up to 860px wide and 520px high, limited to 94% of screen width and 80% of screen height.
- Mobile: 94% of screen width and 80% of the visible screen height. Short screens scroll inside the checkout so the result stays accessible.
- The script adds the 3px border, rounded corners and backdrop. There is no top-right close icon; keep the existing left Back control. Do not add another modal, iframe, white wrapper, padding or custom checkout styles.
- Load the hosted script once and call
AutoUpi.opendirectly with the returnedpayment_url. Do not copy or self-host an older checkout.js. - Allow
https://autoupi.inin your site's script-src and frame-src policies if you use Content Security Policy. - Keep API keys and webhook secrets on your server. Keep your existing order creation and payment verification unchanged.
- Use
onSuccessandonFailurefor result updates. The current hosted script does not emit anonClosecallback; do not rely on it to reset your page. A buyer-scoped server status check can reconcile pending orders. - Check one paid order, one failed/expired order, duplicate webhook delivery and the left Back confirmation on desktop and mobile before going live. Callbacks update your page; only server verification authorizes delivery.
PHP integration in 3 files
Use these three server-side files with the HTML / JavaScript popup example above. Store the API key and webhook secret securely, and connect the order-saving and fulfilment hooks to your existing site. WordPress, Laravel and CodeIgniter should use their own session, CSRF and request handling conventions.
<?php
class AutoUpi {
private string $key;
private string $base;
public function __construct(string $key, string $base = "https://autoupi.in/api/public/v1") {
$this->key = $key;
$this->base = $base;
}
private function request(string $url, ?array $body = null): array {
$ch = curl_init($url);
$headers = ["X-API-Key: {$this->key}"];
if ($body !== null) {
$headers[] = "Content-Type: application/json";
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 20]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
return is_array($res) ? $res : ["ok" => false, "error" => "network_error"];
}
public function createOrder(float $amount, string $customer = "", string $webhook = ""): array {
return $this->request($this->base . "/create-order", [
"amount" => $amount,
"customer_name" => $customer,
"webhook_url" => $webhook,
]);
}
public function status(string $orderId): array {
return $this->request($this->base . "/order-status?order_id=" . urlencode($orderId));
}
public static function verify(string $raw, string $header, string $secret): bool {
parse_str(str_replace(",", "&", $header), $p);
if (empty($p["t"]) || empty($p["v1"])) return false;
return hash_equals(hash_hmac("sha256", $p["t"] . "." . $raw, $secret), $p["v1"]);
}
}<?php
require "autoupi.php";
// Require POST, authenticate the buyer, and validate CSRF using your app.
$api = new AutoUpi(getenv("AUTOUPI_KEY"));
$order = $api->createOrder(1499, "Rahul Sharma", "https://yoursite.com/autoupi-webhook.php");
if (empty($order["ok"])) {
http_response_code(400);
header("Content-Type: application/json");
echo json_encode(["ok" => false, "error" => "Could not start payment"]);
exit;
}
// store it so the webhook can find the buyer later
save_order($order["order_id"], $_SESSION["user_id"], $order["amount"]);
// save_order is your application's function, not an Auto Upi SDK method.
header("Content-Type: application/json");
echo json_encode(["ok" => true, "order_id" => $order["order_id"], "payment_url" => $order["payment_url"]]);
exit;<?php
require "autoupi.php";
$raw = file_get_contents("php://input");
$sig = $_SERVER["HTTP_X_AUTOUPI_SIGNATURE"] ?? "";
if (!AutoUpi::verify($raw, $sig, getenv("AUTOUPI_WEBHOOK_SECRET"))) {
http_response_code(401);
exit("bad signature");
}
$event = json_decode($raw, true);
if (($event["event"] ?? "") === "payment.paid") {
// amount = your original order value; payable_amount = what the customer actually paid
// Look up the saved order and verify its amount before fulfilment.
// Your mark_order_paid function must handle duplicate delivery atomically.
mark_order_paid($event["order_id"], $event["amount"]);
}
http_response_code(200);
echo "ok";No webhook endpoint yet? Your page can call your own server to check order-status while the popup is open and after a result. Authenticate the buyer, check order ownership and fulfil once only when the server confirms paid. Never put the API key in that page.
Errors
| HTTP | Code | Fix |
|---|---|---|
| 401 | invalid_api_key | Key missing, revoked or replaced by a regenerate. |
| 400 | invalid_amount | Amount must be greater than 0 and up to 10,00,000. |
| 400 | UPI_NOT_CONFIGURED | Save a UPI ID on Connect Accounts first. |
| 400 | QUOTA_EXCEEDED | QR limit finished — upgrade to Pro. |
| 400 | ALL_PAYMENT_SLOTS_BUSY | Too many pending orders at the same amount; retry shortly. |
| 404 | not_found | The order ID does not belong to this key. |
Pricing
No per-transaction commission — you collect straight into your own UPI account.
Free
₹0to get started- 3 payment QR codes total
- 1 merchant account
- Full REST API access
- Automatic payment confirmation
Pro
₹299per 30 days- 3,000 QR codes per 30 days
- Unlimited payment links
- Signed webhooks
- Priority support
Custom
Talk to ushigh volume- Higher QR limits
- Multiple merchant accounts
- Self-hosted deployment
- Dedicated support
FAQ
No. Money reaches your own UPI account directly; Auto Upi only confirms and records the order.
They can't — each active order carries its own unique payable_amount.
5 minutes. After that the order becomes expired and moves to Transactions.
PHP 7.4 or newer with the cURL extension — no composer package required.
Yes. Regenerating issues a new key and instantly disables the previous one.
Auto UpiDOCS