Pay per call with HTTP 402
NextCharge agents per API call with a 402 challenge, paid through TatvaPay and checked on your own server.
Next (sandbox). HTTP 402 works on the sandbox today, on the fake rail. Pre-funded Reserve Pay blocks and batched metering come with the payment partner.
An API that charges per call answers a call without proof with a challenge, in TatvaPay's header and in x402 version 2 form:
HTTP/1.1 402 Payment Required
TatvaPay-Payment-Required: scheme="tatvapay-v1", quote="qt_…", amount="1000",
currency="INR", pay="https://api.tatvapay.com/v1/pay/intents", expires="…"
PAYMENT-REQUIRED: <base64 JSON: {"x402Version": 2, "accepts": [{"scheme": "tatvapay-upi",
"network": "upi:in", "amount": "1000", "asset": "INR", "payTo": "agt_…", …}]}>
The buyer agent pays the quote under its mandate. The payment comes back with a payment proof: a short token signed by TatvaPay. The agent calls again with it:
TatvaPay-Payment: proof="<payment proof>"
or, the x402 way, PAYMENT-SIGNATURE carrying {"x402Version": 2, "accepted": {"scheme": "tatvapay-upi"}, "payload": {"proof": "…"}}.
Your server checks it alone
A proof names the seller, the path, the amount and an expiry, and is signed with TatvaPay's published key (/.well-known/tatvapay-keys.json). Your server checks it without calling TatvaPay: the signature, that it is to you, for this path, for at least your price, and not expired. One proof buys one call: keep the proofs you have used, or, when your API runs on several servers, call POST /v1/pay/402/redeem once per proof and TatvaPay refuses the second use.
Settlement is in rupees on UPI through the licensed partner. Crypto settlement is not offered in India.
Buyer: one line
from tatvapay.http402 import fetch_paid
response = fetch_paid(agent, httpx.Client(), "GET", "https://api.seller.example/fare-alerts/DEL-BOM",
max_amount_paise=5000)
import { fetchPaid } from "@tatvapay/sdk";
const response = await fetchPaid(agent, "https://api.seller.example/fare-alerts/DEL-BOM", { maxAmountPaise: 5000 });
Seller: a paywall
from tatvapay.http402 import PROOF_HEADER, Paywall, ProofRefused
paywall = Paywall(seller_agent, amount_paise=1000, description="Fare alert")
def handler(request):
proof = request.headers.get(PROOF_HEADER)
if proof:
try:
paid = paywall.redeem(proof, request.url.path)
return {"alert": "…", "paid_with": paid.payment_id}
except ProofRefused as exc:
reason = str(exc)
status, headers, body = paywall.payment_required(request.url.path)
return Response(body, status, headers)
redeem checks the proof with the published keys only. Give used= a shared store, or remote_single_use=True, if your API runs in more than one process. Without the SDK: verify_payment_proof(proof, jwks, seller_agent_id=…, resource=…, amount_paise=…) (Python) or verifyPaymentProof (TypeScript).
The full scheme, field by field, is in the engine repository at docs/protocols/http402-upi-scheme.md.
Last updated 1 October 2026
Something unclear or wrong? Tell us