InPay Developer Documentation
Integrate InPay into your website or app. Accept UPI payments and send bank transfers via IFSC codes. India's professional payment gateway with INR settlement & USDT cash-out options.
https://inpay.45.63.23.31.nip.io/api/v1Overview
InPay is a professional India-focused payment gateway built for developers. Every endpoint is RESTful, returns JSON, and uses Bearer token authentication. The platform supports 1 pay-in method (UPI — Unified Payments Interface) and 1 payout method (bank transfer via IFSC code). Settlements are available in USDT (min $2,000, $100 flat fee).
12%
Deducted from gross
5%
Added on top
$100 flat
Min $2,000 USDT
Quick Start
Three steps to your first payment:
- 1Create an account at https://inpay.45.63.23.31.nip.io
- 2Generate an API key from Dashboard → API Keys → Create. Keys start with
ip_live_ - 3Make your first request using the Bearer token header. Test balance first, then create a UPI pay-in:
curl https://inpay.45.63.23.31.nip.io/api/v1/balance \
-H "Authorization: Bearer ip_live_<your_48_char_hex_key>"Authentication
All API requests require a Bearer token. Generate keys from the dashboard under API Keys → Create. Each key is a 48-character hex string prefixed with ip_live_, giving 192 bits of entropy.
An API key can only access the balance & history of its own account. You can never withdraw funds from another user — your balance is checked atomically before every payout.
Authorization header
Authorization: Bearer ip_live_a1b2c3d4e5f6...48hex_chars_totalSample authenticated request
curl https://inpay.45.63.23.31.nip.io/api/v1/balance \
-H "Authorization: Bearer ip_live_<your_48_char_hex_key>"API Endpoints Reference
Every endpoint is rooted at https://inpay.45.63.23.31.nip.io/api/v1.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /balance | Get current account balance, totals, currency | |
| GET | /methods | List all pay-in & payout methods + fee rates | |
| POST | /payin | Create a UPI pay-in — returns payment URL/QR | |
| POST | /payout | Send a bank transfer (balance checked atomically) | |
| GET | /transactions | List your pay-in & payout history |
Deposit (Pay-in) Methods
InPay supports UPI — India's unified payment interface used by 400M+ users across 200+ banks.
| Method ID | Label | Min | Max | Fee | Time |
|---|---|---|---|---|---|
| upi | UPI (Unified Payments Interface) | ₹500 | ₹1,00,000 | 12% | Instant - 1 min |
Preset amounts (INR)
UPI accepts the following preset amounts:
Withdrawal (Payout) Methods
Send bank transfers using IFSC codes — works with any Indian bank (NEFT/IMPS rails). Balance is checked atomically before the transfer — if it fails, the debited amount is refunded instantly.
| Method ID | Label | Min | Max | Destination Format | Fee |
|---|---|---|---|---|---|
| bank | Bank Transfer (IFSC) | ₹500 | ₹1,00,000 | IFSC + accountNo + accountNamee.g. HDFC0001234 | 12345678901 | John Doe | 5% |
IFSC is 11 characters: 4 letters (bank) + 0 + 6 alphanumeric (branch). Example: HDFC0001234. Account number must be 8-18 digits. Account holder name must match bank records.
The server locks your balance in a database transaction before calling the payout provider. If the provider call fails for any reason, the full amount (incl. fee) is refunded automatically — no manual reconciliation needed.
Code Examples
Production-ready snippets in curl, JavaScript, and Python.
1 · Create Pay-in (UPI)
curl -X POST https://inpay.45.63.23.31.nip.io/api/v1/payin \
-H "Authorization: Bearer ip_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"method": "upi",
"payerEmail": "customer@example.com",
"payerName": "Rahul Verma"
}'// Create a UPI pay-in (returns payment URL + intent)
const res = await fetch("https://inpay.45.63.23.31.nip.io/api/v1/payin", {
method: "POST",
headers: {
"Authorization": "Bearer ip_live_<your_key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 500, // INR
method: "upi", // only UPI supported
payerEmail: "customer@example.com",
payerName: "Rahul Verma",
}),
});
const data = await res.json();
if (!data.error) {
console.log("Reference:", data.reference);
console.log("Payment URL:", data.paymentUrl);
console.log("QR / Intent:", data.qrCode);
console.log("Fee:", data.methodFee, "Net:", data.amountNet);
// Redirect user to data.paymentUrl to complete UPI payment
} else {
console.error("Error:", data.error);
}import requests
res = requests.post("https://inpay.45.63.23.31.nip.io/api/v1/payin",
headers={
"Authorization": "Bearer ip_live_<your_key>",
"Content-Type": "application/json",
},
json={
"amount": 500,
"method": "upi",
"payerEmail": "customer@example.com",
"payerName": "Rahul Verma",
},
)
data = res.json()
if "error" not in data:
print("Reference:", data["reference"])
print("Payment URL:", data.get("paymentUrl"))
else:
print("Error:", data.get("error"))2 · Create Payout (Bank Transfer)
The server checks your balance before the transfer. If you don't have enough, you get a 400 error and nothing is debited.
curl -X POST https://inpay.45.63.23.31.nip.io/api/v1/payout \
-H "Authorization: Bearer ip_live_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"amount": 2000,
"method": "bank",
"ifsc": "HDFC0001234",
"accountNo": "12345678901",
"accountName": "Rahul Verma"
}'// Send ₹2,000 to a bank account — balance checked atomically first
const res = await fetch("https://inpay.45.63.23.31.nip.io/api/v1/payout", {
method: "POST",
headers: {
"Authorization": "Bearer ip_live_<your_key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 2000, // INR amount to send
method: "bank", // only bank transfer supported
ifsc: "HDFC0001234", // 11-char IFSC: 4 bank + 0 + 6 branch
accountNo: "12345678901", // 8-18 digit account number
accountName: "Rahul Verma", // account holder name
}),
});
const data = await res.json();
if (!data.error) {
console.log("Payout reference:", data.reference);
console.log("Amount:", data.amount);
console.log("Fee:", data.fee, "Total debited:", data.totalDebited);
console.log("New balance:", data.newBalance);
console.log("Status:", data.status); // processing | completed
} else {
// Balance is auto-refunded on failure — no manual reconciliation needed
console.error("Payout failed:", data.error);
}import requests
res = requests.post("https://inpay.45.63.23.31.nip.io/api/v1/payout",
headers={
"Authorization": "Bearer ip_live_<your_key>",
"Content-Type": "application/json",
},
json={
"amount": 2000,
"method": "bank",
"ifsc": "HDFC0001234",
"accountNo": "12345678901",
"accountName": "Rahul Verma",
},
)
data = res.json()
if "error" not in data:
print("Payout sent:", data["reference"])
print("Fee:", data.get("fee"), "| Total debited:", data.get("totalDebited"))
else:
print("Failed (balance auto-refunded):", data.get("error"))3 · Check Balance
curl https://inpay.45.63.23.31.nip.io/api/v1/balance \
-H "Authorization: Bearer ip_live_<your_key>"const res = await fetch("https://inpay.45.63.23.31.nip.io/api/v1/balance", {
headers: { "Authorization": "Bearer ip_live_<your_key>" }
});
const data = await res.json();
// { balance: 50000, totalPaidIn: 200000, totalPaidOut: 75000, currency: "INR" }
console.log("Balance:", data.balance, data.currency);import requests
res = requests.get("https://inpay.45.63.23.31.nip.io/api/v1/balance",
headers={"Authorization": "Bearer ip_live_<your_key>"})
print(res.json())
# {'balance': 50000, 'totalPaidIn': 200000, 'totalPaidOut': 75000, 'currency': 'INR'}4 · List Methods
curl https://inpay.45.63.23.31.nip.io/api/v1/methods \
-H "Authorization: Bearer ip_live_<your_key>"const res = await fetch("https://inpay.45.63.23.31.nip.io/api/v1/methods", {
headers: { "Authorization": "Bearer ip_live_<your_key>" }
});
const data = await res.json();
console.log("Currency:", data.currency);
console.log("Pay-in rate:", data.fees?.payin);
console.log("Payout rate:", data.fees?.payout);import requests
res = requests.get("https://inpay.45.63.23.31.nip.io/api/v1/methods",
headers={"Authorization": "Bearer ip_live_<your_key>"})
data = res.json()
print("Currency:", data.get("currency"))
print("Fees:", data.get("fees"))5 · List Transactions
curl "https://inpay.45.63.23.31.nip.io/api/v1/transactions?type=payin&limit=50" \
-H "Authorization: Bearer ip_live_<your_key>"const res = await fetch("https://inpay.45.63.23.31.nip.io/api/v1/transactions?type=payin&limit=50", {
headers: { "Authorization": "Bearer ip_live_<your_key>" }
});
const data = await res.json();
// { payins: [...], payouts: [...] } (filtered by ?type=payin|payout)
console.log("Pay-ins:", data.payins?.length || 0);
console.log("Payouts:", data.payouts?.length || 0);import requests
res = requests.get("https://inpay.45.63.23.31.nip.io/api/v1/transactions?limit=50",
headers={"Authorization": "Bearer ip_live_<your_key>"})
data = res.json()
print(f"Pay-ins: {len(data.get('payins', []))}, Payouts: {len(data.get('payouts', []))}")6 · Handle Webhook
// Express.js webhook handler — verifies payment status from InPay
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/inpay", (req, res) => {
const event = req.body;
// Verify status is "completed" (not just "pending")
if (event.status === "completed" || event.status === "success") {
const reference = event.reference; // PIN-XXXX-XXXX
const amount = event.amountGross; // gross INR
const net = event.amountNet; // net after 12% fee
const method = event.method; // upi
// IDEMPOTENCY: webhooks can be delivered multiple times.
// Always check if you already processed this reference before
// crediting the user. Use a unique constraint on 'reference'.
fulfillOrder(reference, amount, net);
}
// Always return 200 quickly so InPay doesn't retry
res.status(200).json({ received: true });
});
app.listen(3000);# Flask webhook handler — verifies payment status from InPay
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.post("/webhooks/inpay")
def webhook():
event = request.json
status = event.get("status")
if status in ("completed", "success"):
reference = event["reference"]
amount = event["amountGross"]
net = event["amountNet"]
method = event["method"]
# IDEMPOTENCY: check if reference already processed
fulfill_order(reference, amount, net)
# Always return 200 quickly
return jsonify({"received": True}), 200
app.run(port=5000)Webhooks
InPay sends a POST webhook to your configured notify_url when a UPI pay-in is confirmed. Configure your webhook URL via the dashboard or contact support.
Payload shape
{
"event": "payin.completed",
"reference": "PIN-LX7K2P-9F3A21",
"status": "completed", // also accept "success"
"amountGross": 500, // gross INR before fee
"amountNet": 440, // net after 12% fee
"fee": 60,
"method": "upi",
"currency": "INR",
"mchOrderNo": "MCH...", // Airpays order number
"timestamp": "2026-08-24T17:30:58.000Z",
"signature": "sha256=..." // HMAC of payload body
}Verification
- Only fulfill orders when
status === "completed"or"success" - Ignore
pending&failedevents - Return HTTP 200 within 5 seconds — otherwise we retry
- Webhooks may be delivered multiple times — implement idempotency by deduplicating on
reference - Retries happen at: 1m, 5m, 30m, 2h, 12h, 24h
Security
Balance is locked & decremented in a DB transaction before the bank payout call. Failures auto-refund.
12% pay-in & 5% payout fees computed on the server. Clients cannot fake or override them.
API keys are 48-char hex (192 bits). Brute-forcing is computationally infeasible.
All API traffic is TLS 1.3 encrypted. Plain HTTP is rejected at the edge.
60/min general, 10/min pay-in, 5/min payout. Exceeding returns 429.
IFSC code (11 chars, 4-letter bank + 0 + 6 alphanumeric) & account number (8-18 digits) validated server-side.
Error Codes
All errors return JSON: { "error": "message" }
| Status | Name | Description |
|---|---|---|
| 400 | BAD_REQUEST | Malformed JSON, invalid amount, or unsupported method |
| 401 | UNAUTHORIZED | Missing or invalid API key (must start with ip_live_) |
| 400 | INVALID_AMOUNT | Amount out of range (₹500 - ₹1,00,000) or wrong method |
| 400 | INVALID_IFSC | IFSC code format wrong (expected HDFC0000001 — 4 letters + 0 + 6 alphanum) |
| 400 | INVALID_ACCOUNT | Bank account number invalid (8-18 digits) |
| 400 | INSUFFICIENT_BALANCE | Not enough balance for payout (auto-refunded if debited) |
| 403 | FORBIDDEN | API key disabled or method not enabled for your account |
| 429 | RATE_LIMITED | Too many requests — 60/min general, 10/min payin, 5/min payout |
| 500 | INTERNAL_ERROR | Server error — balance refunded automatically if mid-payout |
| 503 | PROVIDER_DOWN | Airpays (UPI) / bank payout provider temporarily down |
InPay API v1 · India · INR · https://inpay.45.63.23.31.nip.io
Need help? Contact support from your dashboard. API keys never expire — rotate from the dashboard anytime.