Collect payments on your website or app. Create payment links, redirect customers to UniWeb checkout, poll status, and receive signed webhooks — all with your UniWeb merchant keys. Bank and payment-rail credentials stay on UniWeb; you never handle partner secrets.
uw_test_… or uw_live_…) and secret (uws_…) from Dashboard → API Settings.https://uniweb.co.in/api/v1/ with headers X-API-Key, X-API-Secret, and Content-Type: application/json.Idempotency-Key header so retries never double-charge.payment_url (UniWeb hosted checkout).X-UniWeb-Signature on every delivery.POST https://uniweb.co.in/api/v1/
Legacy alias https://uniweb.co.in/api.php accepts the same requests. Every JSON response includes api_version: "v1".
| Header | Required | Description |
|---|---|---|
| X-API-Key | Yes | Merchant API key — uw_test_… (sandbox) or uw_live_… (live) |
| X-API-Secret | Yes | Paired secret — uws_… |
| Idempotency-Key | Write calls | Unique string (max 100 chars) for create_payment_link and create_refund |
| Content-Type | Yes | application/json |
Mode is determined by the key prefix — not a separate header. Test keys only return test data and test checkout; live keys require completed KYC and live activation. Browser calls must allowlist your origin under API Settings.
action: create_payment_link · scope: links:write · Idempotency-Key required
Creates a one-time payment link. Redirect the customer to payment_url — UniWeb hosted checkout handles UPI, cards, and netbanking. No partner-branded buttons are shown to your customer.
Request body:
{
"action": "create_payment_link",
"amount": 500,
"description": "Order #123",
"customer_phone": "9876543210",
"customer_name": "Rahul Sharma"
}
cURL:
curl -X POST 'https://uniweb.co.in/api/v1/' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: uw_test_your_key_here' \
-H 'X-API-Secret: uws_your_secret_here' \
-H 'Idempotency-Key: order-123-attempt-1' \
-d '{"action":"create_payment_link","amount":500,"description":"Order #123","customer_phone":"9876543210"}'
Success response (HTTP 200):
{
"success": true,
"api_version": "v1",
"mode": "test",
"link_id": "LNK20260826123456",
"payment_url": "https://uniweb.co.in/checkout.php?link=LNK20260826123456",
"amount": 500,
"expires_at": "2026-08-27 18:30:00"
}
Error example (HTTP 400):
{
"success": false,
"error_code": "amount_out_of_range",
"error": "Amount must be between 1 and 200000000.",
"api_version": "v1"
}
Fields: amount (required, INR, 1–200000000) · description (optional, max 255) · customer_phone / customer_name (optional). Links expire after 24 hours.
action: check_status · scope: transactions:read
Poll a transaction after checkout. Use the txn_id from webhooks or your dashboard. Status is scoped to your API key mode (test vs live).
Request body:
{
"action": "check_status",
"txn_id": "TXN20260826123456"
}
cURL:
curl -X POST 'https://uniweb.co.in/api/v1/' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: uw_test_your_key_here' \
-H 'X-API-Secret: uws_your_secret_here' \
-d '{"action":"check_status","txn_id":"TXN20260826123456"}'
Success response (HTTP 200):
{
"success": true,
"api_version": "v1",
"transaction": {
"txn_id": "TXN20260826123456",
"amount": "500.00",
"status": "success",
"payment_method": "upi",
"utr": "123456789012",
"created_at": "2026-08-26 18:05:22"
}
}
Typical status values: pending, success, failed. Prefer webhooks for real-time updates; use polling as a fallback.
Set your HTTPS webhook URL and signing secret in Dashboard → API Settings. UniWeb POSTs JSON to your server when payments complete. Always verify the HMAC signature — never trust the body alone.
When you rotate your signing secret in API Settings, UniWeb keeps the previous secret valid for 48 hours so deliveries in flight still verify. Update your server to accept both secrets during that window, then drop the old one.
Delivery headers:
Content-Type: application/json X-UniWeb-Event: payment.success X-UniWeb-Event-Id: EVT20260826123456 X-UniWeb-Signature: <hmac-sha256-hex-of-raw-body> User-Agent: UniWeb-Webhook/1.0
Payload body:
{
"id": "EVT20260826123456",
"event": "payment.success",
"created_at": "2026-08-26T12:34:56+00:00",
"data": {
"txn_id": "TXN20260826123456",
"amount": 500,
"status": "success",
"payment_method": "upi",
"utr": "123456789012",
"link_id": "LNK20260826123456"
}
}
Events: payment.success · payment.failed · refund.completed · webhook.test (from API Settings → Send Test Webhook).
Verify signature (PHP — copy-paste):
<?php
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_UNIWEB_SIGNATURE'] ?? '';
$signingSecret = 'your_webhook_signing_secret'; // from API Settings
$expected = hash_hmac('sha256', $raw, $signingSecret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($raw, true);
// Handle $event['event'] and $event['data']
http_response_code(200);
echo 'ok';
Verify signature (Node.js):
const crypto = require('crypto');
function verifyUniWebWebhook(rawBody, signature, signingSecret) {
const expected = crypto.createHmac('sha256', signingSecret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature || ''));
}
// Express example: use express.raw({ type: 'application/json' }) for the route
// const ok = verifyUniWebWebhook(req.body, req.get('X-UniWeb-Signature'), process.env.UNIWEB_WEBHOOK_SECRET);
Failed deliveries retry with exponential backoff (up to 8 attempts). Return HTTP 2xx quickly; process asynchronously if needed. X-UniWeb-Event-Id is stable across retries — use it for idempotent handling on your side.
Official UniWeb client libraries — same Merchant API as above, UniWeb brand only. No partner SDK wrappers; your customer never sees bank/PG product names.
Quick start: copy uw_test_… + uws_… from Dashboard → API Settings → install an SDK below → call createPaymentLink → redirect to payment_url. Full README: sdk/php · sdk/node.
Package: uniweb/merchant-sdk
Monorepo: github.com/uniwebadmin/uniweb/sdk/php
Install (path — until Packagist publish):
composer config repositories.uniweb-merchant-sdk path ../uniweb/sdk/php composer require uniweb/merchant-sdk:*
Create payment link:
use UniWeb\Client\Client;
use UniWeb\Client\ClientConfig;
$uniweb = new Client(new ClientConfig(
apiKey: 'uw_test_your_key_here',
apiSecret: 'uws_your_secret_here',
mode: ClientConfig::MODE_TEST,
));
$link = $uniweb->createPaymentLink([
'amount' => 500,
'description' => 'Order #123',
'customer_phone' => '9876543210',
]);
header('Location: ' . $link['payment_url']);
Package: uniweb
Monorepo: github.com/uniwebadmin/uniweb/sdk/node
Install from Git (dist included):
npm install github:uniwebadmin/uniweb#main:sdk/node # Local checkout: npm install /path/to/uniweb1/sdk/node
Create payment link:
import { Client } from 'uniweb';
const uniweb = new Client({
apiKey: 'uw_test_your_key_here',
apiSecret: 'uws_your_secret_here',
mode: 'test',
});
const link = await uniweb.createPaymentLink({
amount: 500,
description: 'Order #123',
customer_phone: '9876543210',
});
console.log(link.payment_url);
Methods (POST body action matches openapi.json): createPaymentLink, checkStatus, createRefund, getBalance, listTransactions, listRefunds, listPaymentLinks, getPaymentLink. Write calls send a unique Idempotency-Key header automatically. The SDK never logs your API secret.
Error handling — read stable error_code:
// PHP
try {
$link = $uniweb->createPaymentLink(['amount' => 500]);
} catch (\UniWeb\Client\Exception\ApiException $e) {
// $e->errorCode e.g. amount_out_of_range
}
// Node
try {
await uniweb.createPaymentLink({ amount: 500 });
} catch (err) {
if (err.errorCode) console.error(err.errorCode);
}
Webhook verify — header X-UniWeb-Signature = HMAC-SHA256(raw JSON body, signing secret from API Settings):
// PHP SDK
use UniWeb\Client\Webhook;
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_UNIWEB_SIGNATURE'] ?? '';
if (!Webhook::verifySignature($raw, $sig, $signingSecret)) {
http_response_code(401); exit;
}
// Node SDK — use raw body string, not parsed JSON
import { verifySignature } from 'uniweb';
const ok = verifySignature(rawBody, req.headers['x-uniweb-signature'], signingSecret);
During signing-secret rotation, pass the previous secret as the optional fourth argument (PHP) or third optional param (Node) for a 48-hour grace window — same as the raw HMAC examples in Webhooks above.
uw_test_…uw_live_…A test key cannot capture live payments. Responses include "mode": "test" or live-scoped data matching your key. Rotate keys from API Settings if compromised.
Every error response is JSON with stable error_code (for your code) and human-readable error (for logs). HTTP status matches the severity.
| error_code | HTTP | Message |
|---|---|---|
| invalid_json | 400 | Request body must be valid JSON. |
| unknown_action | 400 | Unknown action. See API documentation for supported actions. |
| missing_credentials | 401 | X-API-Key and X-API-Secret headers are required. |
| auth_failed | 401 | Invalid API credentials or insufficient scope. |
| auth_invalid | 401 | Partner rejected credentials during refund processing. Contact UniWeb support. |
| origin_not_allowed | 403 | Origin not allowed for this API key. |
| mode_mismatch | 403 | Account is in Test Mode. Use a test API key or complete KYC for live operations. |
| not_found | 404 | Resource not found. |
| validation_error | 400 | One or more fields failed validation. |
| amount_out_of_range | 400 | Amount must be between 1 and 200000000. |
| description_too_long | 400 | Description is too long (max 255 characters). |
| missing_txn_id | 400 | txn_id is required. |
| missing_link_id | 400 | link_id is required. |
| missing_idempotency_key | 400 | Idempotency-Key header is required for this action. |
| idempotency_conflict | 409 | Idempotency-Key conflict — different request body or an identical request is already in progress. |
| rate_limited | 429 | API rate limit exceeded. Retry after the Retry-After interval. |
| method_not_allowed | 405 | Only POST is supported. |
| refund_failed | 400 | Refund could not be processed. |
| refund_not_allowed | 400 | Refund is not allowed for this payment. |
| txn_not_refundable | 404 | Successful transaction not found for refund. |
| partner_unavailable | 503 | Payment partner is temporarily unavailable. Try again shortly. |
| internal_error | 500 | An internal error occurred. Support has been notified. |
HTTP summary: 200 success · 400 bad request (incl. missing_idempotency_key) · 401 auth (auth_failed = your API key; auth_invalid = partner rejected refund) · 403 mode/origin · 404 not found · 405 method · 409 idempotency conflict · 429 rate limit (see Retry-After header, seconds) · 500 internal · 503 partner unavailable.
All actions use POST to the same URL with an action field in the JSON body.
action: create_payment_link
{"action":"create_payment_link","amount":500,"description":"Order #123","customer_phone":"9876543210"}
Write — Idempotency-Key required. Returns payment_url.
action: check_status
{"action":"check_status","txn_id":"TXN..."}
Read single transaction by txn_id.
action: list_transactions
{"action":"list_transactions","from":"2026-08-01","to":"2026-08-15","limit":20,"offset":0}
Optional date range (YYYY-MM-DD). Paginated — limit max 100.
action: get_balance
{"action":"get_balance"}
Collected and available settlement balance for current mode.
action: create_refund
{"action":"create_refund","txn_id":"TXN...","amount":100,"reason":"Customer request"}
Write — Idempotency-Key required. Omit amount for full refund.
action: list_refunds
{"action":"list_refunds","limit":20,"offset":0}
Paginated refund history.
action: list_payment_links
{"action":"list_payment_links","limit":20,"offset":0}
Paginated links with view_count.
action: get_payment_link
{"action":"get_payment_link","link_id":"LNK..."}
Single link including payment_url.
Per API credential. Short burst allowed.
Use exponential backoff on rate-limit responses.
Reusing the same Idempotency-Key with an identical body returns the original response. Reusing with a different body returns 409 idempotency_conflict. Omitting the header on write actions returns 400 missing_idempotency_key. Keys are scoped per merchant + mode and stored for 72 hours in api_idempotency_keys.
Written exception (parked): there is no public REST action to create a merchant or poll KYC status. Onboarding stays on the UniWeb website — signup, admin invite, and KYC UI. When a named bank or fintech deal requires programmatic onboarding, this page and OpenAPI will be extended.