Pay By AI REST API
All endpoints live under https://www.paybymyai.com/api. JSON in, JSON out. HTTPS required. Every Stripe-touching route respects the current mode (test / live); the mode is derived from the caller's authentication where applicable.
Authentication
Three authentication schemes, depending on the caller.
Authorization: Bearer pbai_mk_test_a1b2c3...Created at merchant onboarding. Mode is encoded in the prefix (pbai_mk_test_ vs pbai_mk_live_). Used for every write on /payment_requests.
Authorization: Bearer pbai_sess_abc123...Minted by the user in the dashboard. Mode is stored on the session record (not the prefix). Used only for /api/mcp — agents cannot call REST endpoints directly.
No header requiredA payment-request token is the credential for reading or paying that one request. GET /payment_requests/{token} and POST /payment_requests/{token}/pay need no other auth.
Error format
All errors return JSON with error (machine-readable code) and message (human-readable). The HTTP status matches the code's severity.
{
"error": "invalid_api_key",
"message": "unknown merchant API key"
}missing_bearerinvalid_api_keyinvalid_tokenrevokedexpirednot_foundinvalid_statespending_cap_exceededmerchant_not_allowedMerchant operations
Called by your backend or e-commerce plugin. All require a merchant API key.
Mint a payment token.
Creates a PaymentRequest the buyer or their AI can pay. The response's id is the bearer token.
{
"amountCents": 1500, // required — integer cents
"description": "Large pizza", // required — ≤ 500 chars
"currency": "usd", // optional — defaults to "usd"
"merchantMetadata": { // optional — arbitrary JSON object
"order_id": "WOO-42",
"cart": ["item-1"]
},
"expiresInSeconds": 900 // optional — 1..86400; default: none
}{
"id": "pr_kXf7z8...",
"merchantId": "cmabc123",
"mode": "test",
"amountCents": 1500,
"currency": "usd",
"description": "Large pizza",
"status": "pending",
"merchantMetadata": { "order_id": "WOO-42" },
"stripePaymentIntentId": null,
"expiresAt": "2026-04-21T00:15:00.000Z",
"paidAt": null,
"createdAt": "2026-04-21T00:00:00.000Z"
}curl -X POST https://www.paybymyai.com/api/payment_requests \
-H "Authorization: Bearer pbai_mk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"amountCents": 1500, "description": "Large pizza"}'Cancel a pending payment request.
Flips status to canceled. Merchants can only cancel their own tokens — token ownership is verified against the authenticated merchant.
{ "id": "pr_...", "status": "canceled", ... }404 not_found— token not owned by the authenticated merchant (or doesn't exist).409 invalid_state— alreadypaidorcanceled.
Create a new merchant + Stripe Connect account.
Used by the web dashboard's onboarding flow. Plugins can call it too, but typically will redirect the merchant to our hosted form.
{
"name": "Joe's Pizza",
"returnUrl": "https://your-plugin.com/paybyai/done",
"refreshUrl": "https://your-plugin.com/paybyai/retry"
}{
"merchantId": "cmabc...",
"stripeAccountId": "acct_...",
"apiKey": "pbai_mk_test_...", // ← show once, store securely
"webhookSigningKey": "pbai_whsec_...", // ← show once, store securely
"onboardingUrl": "https://connect.stripe.com/..."
}apiKey and webhookSigningKey are shown exactly once. If you lose them, rotate via the merchant dashboard (the old values stop working immediately).Return the merchant owning the API key.
Plugins use this to verify a pasted API key belongs to a real, active merchant. No body; just confirms the key.
{
"id": "cmabc...",
"name": "Joe's Pizza",
"mode": "test",
"stripeAccountId": "acct_...",
"webhookUrl": null,
"createdAt": "2026-04-18T23:00:00.000Z"
}Buyer & agent operations
Called by the person (or AI) who is paying. The token itself grants read access; actually paying requires the user's payment method.
Preview a payment request.
Anyone with the token can read the amount, merchant reference, description, and status. Safe to share in UI.
{
"id": "pr_...",
"merchantId": "cmabc...",
"merchant": {
"id": "cmabc...",
"name": "Joe's Pizza",
"stripeAccountId": "acct_..."
},
"mode": "test",
"amountCents": 1500,
"currency": "usd",
"description": "Large pizza",
"status": "pending",
"expiresAt": "...",
"createdAt": "..."
}Pay a token with a stored payment method.
Creates a Stripe PaymentIntent with transfer_data.destination pointing at the merchant's Connect account. Requires a payment method that belongs to the specified user.
In practice, the MCP endpoint is the cleaner way for an AI to pay — it handles session-based authorization and consent checks. This REST endpoint exists for headless or scripted integrations.
{
"userId": "cmuser...",
"paymentMethodId": "cmpm..."
}{
"paymentRequestId": "pr_...",
"paymentIntentId": "pi_...",
"status": "succeeded"
}409 invalid_state— token is notpending(already paid / canceled / expired).404 not_found— payment method doesn't belong to the given user.
Webhooks
Stripe webhook receiver.
Receives Stripe events. Mode is encoded in the path so we can pick the right signing secret for verification. Configure one endpoint per mode in your Stripe dashboard.
payment_intent.succeeded— marks the payment request paid, creates aTransactionrow, and fires the merchant webhook.
On payment_intent.succeeded we POST a signed JSON body to the merchant's configured URL. Headers: X-PayByAI-Signature: t=<ts>,v1=<hmac_sha256> and X-PayByAI-Event-Id: evt_<uuid>. Full format + verify snippets live in /docs/merchants.
Service + misc
Health check — always returns 200 if the app is up.
{ "ok": true }Set the dashboard's active mode cookie.
Only used by the dashboard's TEST/LIVE toggle. Plugins should not call this — pass X-Pbai-Mode: test|live per request instead if they need to override mode (but normally the mode is derived from the merchant API key).
{ "mode": "test" | "live" }