PaynorAI API Documentation
Agent-native payment control with policy checks, approvals, provider execution handoff, and audit trails.
Quick Start — get your agent paying in 5 minutes
First ask PaynorAI for clearance. If approved, your own secure backend charges Stripe/Razorpay, then reports the result back.
curl -X POST /api/public/pay \
+ -H "Content-Type: application/json" \
+ -d '{
"agent_key": "agp_live_xk29...",
"merchant": "aws.amazon.com",
"merchant_category": "Cloud",
"amount": 39,
"currency": "USD",
"provider": "stripe",
"idempotency_key": "order_123"
}'Authentication — how agent_key works
Use your agent key server-side only. PaynorAI hashes the key and matches it to the agent policy before any payment is cleared for your provider.
Authorization model
1. Create an agent in the dashboard
2. Copy the key once: agp_live_...
3. Store it in your server secrets
4. Send it as agent_key with each payment requestPOST /pay — request + response
Approved requests are only cleared for execution. Money moves later through your own Stripe/Razorpay backend, never inside PaynorAI.
POST /api/public/pay
Request
{
"agent_key": "agp_live_xk29...",
"merchant": "aws.amazon.com",
"merchant_category": "Cloud",
"amount": 39,
"currency": "USD",
"provider": "stripe",
"idempotency_key": "order_123"
}
Approved response
{
"status": "approved",
"transaction_id": "tx_...",
"provider": "stripe",
"execute_with_provider": {
"provider": "stripe",
"merchant": "aws.amazon.com",
"amount": 39,
"currency": "USD",
"idempotency_key": "order_123"
},
"next_step": "execute_provider_charge",
"report_result_url": "/api/public/provider-result",
"message": "Policy cleared. Execute this payment with the connected provider from your secure backend, then report the provider result back to PaynorAI."
}Provider execution — the 3-step flow
PaynorAI is the approval gate, not the money mover. Charge cards, banks, UPI, or mandates only from your own secure provider integration after approval.
1. POST /api/public/pay
- If blocked: stop, do not charge
- If pending_approval: wait, do not charge
- If approved: continue
2. Execute the charge in your secure backend
- Stripe PaymentIntent / invoice / subscription
- Razorpay order / payment / mandate
3. POST /api/public/provider-result
- Save provider_transaction_id
- Save receipt_url
- Mark provider_succeeded or provider_failedPOST /provider-result — report real provider outcome
After your backend executes the real Stripe/Razorpay charge, report the provider result so PaynorAI keeps the audit trail accurate.
POST /api/public/provider-result
{
"agent_key": "agp_live_xk29...",
"transaction_id": "tx_...",
"provider": "stripe",
"provider_transaction_id": "pi_123",
"status": "succeeded",
"receipt_url": "https://pay.stripe.com/receipts/..."
}
Response
{
"status": "provider_succeeded",
"transaction_id": "tx_...",
"provider": "stripe",
"provider_transaction_id": "pi_123"
}Do not do these things
These rules keep PaynorAI as a control layer instead of turning it into a payment processor or wallet.
Do not put agent_key in browser code
Do not charge before PaynorAI returns approved
Do not store card, bank, CVV, UPI, or mandate secrets in PaynorAI
Do not treat approved as paid
Do not top up funds into PaynorAI
approved = provider may now charge
provider_succeeded = real provider payment succeededGET /tx/:id — check transaction status
Poll a transaction to know whether it was approved, blocked, rejected, or is still waiting for human review.
GET /api/public/check-tx/TRANSACTION_ID?agent_key=agp_live_xk29...
{
"id": "TRANSACTION_ID",
"status": "pending_approval",
"policy_reason": "Above approval threshold",
"approved_at": null
}Approvals — release or reject a held payment
When a transaction crosses the approval threshold, send an approval decision before your connected provider is allowed to execute it.
POST /api/public/approve-tx
{
"transaction_id": "tx_...",
"action": "approve",
"agent_key": "agp_live_xk29..."
}Error codes — full list with explanations
Use these responses to decide whether the agent should retry, ask a human, or stop the purchase.
400 invalid_request Missing or invalid request fields
401 invalid_agent_key Key does not match an active agent
403 agent_paused Agent has been disabled
403 per_tx_limit_exceeded Amount is above the single payment cap
403 monthly_limit_exceeded Agent budget is exhausted
403 blocked_merchant Merchant is not allowed
403 category_not_allowed Merchant category is not allowed
403 provider_not_connected Provider is not connected for this owner
409 transaction_not_pending Approval was already resolved
409 transaction_not_provider_ready Charge result reported before approval
202 pending_approval Human approval is required
provider_succeeded Real provider payment succeeded
provider_failed Real provider payment failedSDK-style JavaScript snippet
Call PaynorAI from your own backend so the agent key never appears in browser code.
async function payForAgent({ merchant, amount, orderId }) {
const res = await fetch("/api/public/pay", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
agent_key: process.env.PAYNORAI_AGENT_KEY,
merchant,
merchant_category: "Cloud",
amount,
currency: "USD",
provider: "stripe",
idempotency_key: orderId,
}),
});
const result = await res.json();
if (result.status !== "approved") return result;
const providerPayment = await chargeWithStripeOrRazorpay(result.execute_with_provider);
await fetch("/api/public/provider-result", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
agent_key: process.env.PAYNORAI_AGENT_KEY,
transaction_id: result.transaction_id,
provider: result.provider,
provider_transaction_id: providerPayment.id,
status: providerPayment.succeeded ? "succeeded" : "failed",
receipt_url: providerPayment.receipt_url,
}),
});
return result;
}