PayByMy.AI
MCP API reference

Model Context Protocol endpoint

Pay By AI exposes an MCP server at POST https://www.paybymyai.com/api/mcp. Any AI agent that speaks the Model Context Protocol can connect with a budget code and spend against it within the consent rules the user set.

Transport

Most MCP clients handle the handshake and framing for you — you just supply the URL and the bearer. This page is the reference if you're writing a custom client or debugging.

Authentication

Send a budget code (internally: agent session bearer) in the Authorization header. The user minted it from the dashboard at /user/budgets and pasted it into your MCP client's config.

Authorization: Bearer pbai_sess_abc123...

Browse + checkout tools (get_merchant, search_merchants, search_products, get_product, checkout, get_payment_request) work without a bearer — agents can window-shop and even mint a payment token before a budget is attached. Tools that touch money or user data (pay_payment_request, list_payment_methods, get_current_budget, list_payments) require a valid, non-revoked, non-expired bearer.

Consent enforcement

Every budget carries three limits the server enforces before creating any Stripe PaymentIntent:

Spending limit

Cumulative cents this budget can spend. spending_cap_exceeded if a single payment or running total would exceed it.

Merchant allowlist

Optional list of allowed merchant IDs. merchant_not_allowed if the target merchant isn't in it. Empty list = any merchant on Pay By AI.

Expiry

expiresAt is an absolute timestamp. Once past, every call returns 401 expired.

Consent is MCP-only. The REST POST /api/payment_requests/{token}/pay endpoint exists for the user to pay directly and does not enforce these limits — because the REST caller is the user, not an agent acting for them.

Handshake

MCP clients start with initialize to negotiate protocol version and capabilities.

Request
POST /api/mcp
Authorization: Bearer pbai_sess_...
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {},
    "clientInfo": { "name": "my-client", "version": "0.1.0" }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "paybyai", "version": "0.0.0" }
  }
}

After initialize, the client may send a notifications/initialized notification. We respond 204 No Content and get to work.

List tools

Request
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
Response
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      { "name": "get_merchant", ... },
      { "name": "search_merchants", ... },
      { "name": "search_products", ... },
      { "name": "get_product", ... },
      { "name": "checkout", ... },
      { "name": "get_payment_request", ... },
      { "name": "pay_payment_request", ... },
      { "name": "list_payment_methods", ... },
      { "name": "get_current_budget", ... },
      { "name": "list_payments", ... }
    ]
  }
}

Tool reference

get_merchantPublic

Look up a merchant by ID.

Input
{
  "id": "string"
}
Output
{
  "id": "string",
  "name": "string",
  "stripeAccountId": "string",
  "mode": "\"test\" | \"live\""
}
search_merchantsPublic

Search merchants by name (case-insensitive substring match).

Input
{
  "q": "string (optional)",
  "limit": "number (optional, max 100, default 20)"
}
Output
Merchant[] — ordered newest first.
search_productsPublic

Browse/keyword-search a merchant's catalog. Pay By AI proxies to the merchant's own commerce MCP (their WooCommerce/Shopify plugin) — agents only need to connect to Pay By AI.

Input
{
  "merchantId": "string (from search_merchants)",
  "q": "string (optional keyword)",
  "limit": "number (optional, 1-50, default 20)",
  "page": "number (optional, 1-based, default 1)"
}
Output
{
  products: [{ id, title, price, currency, availableForSale, imageUrl, ... }],
  page, limit,
  merchantId, merchantName
}
get_productPublic

Fetch a single product's full detail (variants, stock) at a merchant. Proxied to the merchant's commerce MCP.

Input
{
  "merchantId": "string",
  "productId": "string | number"
}
Output
{
  id, title, description, type, price, currency,
  stockQuantity, availableForSale,
  variants: [{ id, attributes, price, availableForSale, stockQuantity }],
  imageUrl,
  merchantId, merchantName
}
checkoutPublic

Create an order at a merchant and mint a Pay By AI payment token — proxied to the merchant's commerce MCP. Agent collects shipping + email from the user and passes them here. Then call pay_payment_request with the returned token.

Input
{
  "merchantId": "string",
  "items": "Array<{ productId, variantId?, quantity }>",
  "email": "string",
  "shippingAddress": "{ firstName, lastName, address1, address2?, city, state, country, postcode, phone? }",
  "note": "string (optional)"
}
Output
{
  token: "pr_... — pass to pay_payment_request",
  amountCents, currency, description,
  merchantId, merchantName
}
Notes

The merchant creates the order in a pending state — it's not fulfilled until Pay By AI webhooks the merchant payment_request.paid after the token is paid.

get_payment_requestPublic

Preview a payment request before paying. The token IS the credential for this tool.

Input
{
  "token": "string"
}
Output
{
  id, merchantId, merchant: { id, name, stripeAccountId },
  amountCents, currency, description, status,
  merchantMetadata, expiresAt, createdAt
}
pay_payment_requestAuth required

Pay a payment request with one of the authenticated user's stored payment methods. Consent-bounded.

Input
{
  "token": "string",
  "paymentMethodId": "string (one of the user's stored PMs)"
}
Output
{
  paymentRequestId: "pr_...",
  paymentIntentId: "pi_...",
  status: Stripe PaymentIntent status (usually "succeeded" or "processing")
}
Notes

Before dispatching the PaymentIntent we check spending_cap_exceeded and merchant_not_allowed. After success we increment the budget's spent counter. Stripe webhooks handle the eventual settle + merchant notification separately.

list_payment_methodsAuth required

List the authenticated user's stored payment methods. Default card is first.

Input

{} — no arguments.

Output
PaymentMethod[] — each with { id, brand, last4, expMonth, expYear, isDefault }
get_current_budgetAuth required

Return this session's own budget — limit, spent, remaining, allowed stores, expiry. Does not reveal the user's other budgets. Use it to answer 'how much can I still spend?' and similar.

Input

{} — no arguments.

Output
{
  limitCents: number | null,        // null = unlimited
  spentCents: number,
  remainingCents: number | null,    // null when limitCents is null
  allowedMerchantIds: string[],     // empty = any merchant
  expiresAt: string | null,         // ISO timestamp or null = no expiry
  createdAt: string                 // ISO timestamp
}
Notes

spentCents reflects the session state loaded at the start of this MCP request. If a payment happened during the same request, the new spend is recorded asynchronously and may not appear in the response. For nearly all user queries this is fine; if you need exact post-pay accuracy, call get_current_budget again in a subsequent turn.

Calling a tool — full example

curl -X POST https://www.paybymyai.com/api/mcp \
  -H "Authorization: Bearer pbai_sess_YOUR_BUDGET_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 42,
    "method": "tools/call",
    "params": {
      "name": "pay_payment_request",
      "arguments": {
        "token": "pr_kXf7z8...",
        "paymentMethodId": "cmpm..."
      }
    }
  }'
Successful response
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"paymentRequestId\":\"pr_kXf7z8...\",\"paymentIntentId\":\"pi_...\",\"status\":\"succeeded\"}"
      }
    ]
  }
}
Error response (consent rule blocked the payment)
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "isError": true,
    "content": [
      { "type": "text", "text": "spending_cap_exceeded: payment of 2000 cents exceeds remaining cap of 500 cents" }
    ]
  }
}
Consent failures are returned as tool-level isError: true rather than JSON-RPC protocol errors, so agents can read the message and adapt. Authentication failures (bad / revoked / expired bearer) are returned as HTTP 401.

Quick client configs

Claude Desktop
{
  "mcpServers": {
    "paybyai": {
      "url": "https://www.paybymyai.com/api/mcp",
      "headers": { "Authorization": "Bearer pbai_sess_YOUR_CODE" }
    }
  }
}
Claude Code CLI
claude mcp add paybyai \
  --transport http \
  --url https://www.paybymyai.com/api/mcp \
  --header "Authorization=Bearer pbai_sess_YOUR_CODE"
Custom client (Python)
import httpx

resp = httpx.post(
    "https://www.paybymyai.com/api/mcp",
    headers={"Authorization": "Bearer pbai_sess_YOUR_CODE"},
    json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
    timeout=10,
)
print(resp.json())
MCP tools for AI agents — Pay By AI