PayByMy.AI
REST API reference

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.

Merchant API key
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.

Agent session (budget) token
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.

Token credential (none)
No header required

A 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_bearer
401 — Authorization header absent
invalid_api_key
401 — bearer does not resolve to a merchant
invalid_token
401 — unknown agent session token
revoked
401 — agent session was revoked
expired
401 — agent session past expiresAt
not_found
404 — resource doesn't exist in current mode
invalid_state
409 — state transition not allowed
spending_cap_exceeded
403 — payment exceeds remaining cap
merchant_not_allowed
403 — merchant not in session allowlist

Merchant operations

Called by your backend or e-commerce plugin. All require a merchant API key.

POST/payment_requestsMerchant API key

Mint a payment token.

Creates a PaymentRequest the buyer or their AI can pay. The response's id is the bearer token.

Body
{
  "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
}
Response — 200
{
  "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
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"}'
POST/payment_requests/{token}/cancelMerchant API key

Cancel a pending payment request.

Flips status to canceled. Merchants can only cancel their own tokens — token ownership is verified against the authenticated merchant.

Response — 200
{ "id": "pr_...", "status": "canceled", ... }
Errors
  • 404 not_found — token not owned by the authenticated merchant (or doesn't exist).
  • 409 invalid_state — already paid or canceled.
POST/merchants/onboardingNo auth

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.

Body
{
  "name": "Joe's Pizza",
  "returnUrl": "https://your-plugin.com/paybyai/done",
  "refreshUrl": "https://your-plugin.com/paybyai/retry"
}
Response — 200
{
  "merchantId": "cmabc...",
  "stripeAccountId": "acct_...",
  "apiKey": "pbai_mk_test_...",           // ← show once, store securely
  "webhookSigningKey": "pbai_whsec_...",  // ← show once, store securely
  "onboardingUrl": "https://connect.stripe.com/..."
}
Both apiKey and webhookSigningKey are shown exactly once. If you lose them, rotate via the merchant dashboard (the old values stop working immediately).
GET/merchants/meMerchant API key

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.

Response — 200
{
  "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.

GET/payment_requests/{token}No auth

Preview a payment request.

Anyone with the token can read the amount, merchant reference, description, and status. Safe to share in UI.

Response — 200
{
  "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": "..."
}
POST/payment_requests/{token}/payNo auth

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.

Body
{
  "userId": "cmuser...",
  "paymentMethodId": "cmpm..."
}
Response — 200
{
  "paymentRequestId": "pr_...",
  "paymentIntentId": "pi_...",
  "status": "succeeded"
}
Errors
  • 409 invalid_state — token is not pending (already paid / canceled / expired).
  • 404 not_found — payment method doesn't belong to the given user.

Webhooks

POST/webhooks/stripe/{mode}Stripe signature

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.

Handled event types
  • payment_intent.succeeded — marks the payment request paid, creates a Transaction row, and fires the merchant webhook.
Outbound: POST <merchant.webhookUrl>

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

GET/healthNo auth

Health check — always returns 200 if the app is up.

{ "ok": true }
POST/modeSession cookie

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" }
REST API reference — Pay By AI