---
title: "Agent Payments (HTTP 402)"
description: "An agent that exhausts its query budget can buy more over HTTP 402 with the Machine Payments Protocol, settle the charge, and carry on without waiting for a human."
canonical: "https://pgbeam.com/docs/agent-payments"
last-updated: "2026-09-07T21:43:42.000Z"
---

# Agent Payments (HTTP 402)

> An agent that exhausts its query budget can buy more over HTTP 402 with the Machine Payments Protocol, settle the charge, and carry on without waiting for a human.

URL: https://pgbeam.com/docs/agent-payments

A query budget stops an agent dead. That is the point of it: a runaway loop hits a ceiling instead of your database. But it also stops an agent that is doing exactly what you asked, at three in the morning, with nobody around to raise the limit.

Agent payments give that agent a way out that does not involve waking you up. When the budget refuses a statement, PgBeam answers with an HTTP 402 carrying a payment challenge. The agent pays, the queries are granted, and it keeps going.

This surface ships switched off. Turning it on takes deliberate configuration
on both the control plane and the data planes, plus a feature flag. See
Turning it on.

## The protocol

PgBeam implements the Machine Payments Protocol (MPP): the `Payment` HTTP authentication scheme built on the 402 status code, co-authored by Tempo and Stripe. Payment method `stripe`, intent `charge`.

The exchange is the standard three steps.

**1. The unpaid request gets a challenge.**

The `request` parameter is base64url of the JCS-canonical charge request. Decoded, it is what you are being asked to pay:

Note `header="Payment-Authorization"`. The endpoint already authenticates you with a bearer token, so the challenge selects the alternate credential field and leaves `Authorization` alone.

**2. Pay, and repeat the request with the credential.**

Create a Stripe Shared Payment Token against your own Stripe account, with usage limits matching the challenge amount, then send the credential:

The credential is base64url of `{"challenge": {...echoed unchanged...}, "payload": {"spt": "spt_..."}}`.

**3. The queries are granted and a receipt comes back.**

The receipt decodes to `{"status":"success","method":"stripe","timestamp":"...","reference":"pi_...","challengeId":"..."}`.

## Discovery

An agent finds the price two ways.

`GET /v1/payments/resources` needs no authentication and lists everything for sale, with the unit, the pack size, and the price of one pack.

The published OpenAPI document at `/openapi.json` carries the MPP discovery extension on the priced operation:

Both are read from the same catalog the settlement path charges from, and a test fails the build if they disagree.

## What you are buying

Resource

Unit

Pack

Price

Per payment

`query_budget_topup`

queries

1,000

$2.00

1 to 25 packs

Purchased queries expire 24 hours after settlement.

They do not raise the policy budget. They are a separate pool the proxy spends only once the hourly and daily windows have already refused a statement, so a top-up can never let an agent run further inside its free allowance than the policy grants, and it is never consumed while the policy would have allowed the statement anyway.

The MCP `budget` block shows the reserve:

## Where the 402 shows up

**The control plane API** is the payment surface. `POST /v1/payments/budget-topups` is a real HTTP 402 with a complete `WWW-Authenticate: Payment` challenge, and it is what an MPP client talks to.

**The MCP endpoint** hands you the challenge when a tool call is refused. The tool result is the MPP JSON-RPC error object with code `-32042`, `httpStatus: 402`, a `challenges` array and a `settlementUrl` naming where to pay. The MCP server also advertises the capability at initialize, under `capabilities.experimental.payment`.

**The PostgreSQL wire protocol cannot carry a 402.** A blocked statement leaves the proxy as an ErrorResponse and there is no field in it for a challenge, so the wire path does the only honest thing available and names the URL:

## Authentication

The endpoint authenticates with the agent credential's own hosted MCP token (`pba_...`), the same one it already uses against the MCP endpoint. The credential identifies itself, so no human, no dashboard session, and no API key is involved. An agent can only buy budget for itself: a challenge issued for one credential is refused if a different one presents it.

Authentication is checked before payment. A caller with no valid token gets `401` and never sees a price.

## Single use

A payment proof is redeemable exactly once.

The challenge identifier is claimed in the database before the charge reaches Stripe, so two concurrent redemptions of one credential resolve to a single charge.

The proof's fingerprint is unique across all challenges, so presenting a spent token against a fresh challenge is refused too.

A challenge that already settled or already failed is consumed. Retrying needs a fresh challenge, which is what the `402` on a failure hands you.

The Stripe idempotency key is derived from the challenge and the proof, so a settlement resumed after an interruption returns the original PaymentIntent rather than charging twice.

A replay is answered `402` with problem type `invalid-challenge` and a fresh challenge, never a receipt.

## Reconciliation

Stripe is the system of record. PgBeam stores the PaymentIntent id and what it granted; it keeps no balance that could disagree with Stripe about how much money moved. Each charge carries the challenge id, the resource, the unit count and the agent credential in its Stripe metadata, and each settlement row carries the organization and project that own the credential.

A top-up is a one-off charge. It does not touch the organization's subscription, its plan limits, or its usage overage.

## If a charge succeeds and the queries are not granted

There is one failure that costs you money: the charge settles at Stripe and the grant cannot be written, for example because the project was retired between the `402` and the payment. That answers `500`, not `402`, and it carries no fresh challenge. The message says the payment has been taken. Do not present the credential again: the settlement row records the PaymentIntent id against the failure, and support can refund the charge or grant the queries by hand. Retrying with a new payment buys the same pack a second time.

## Limits

## The purchase ceiling

One credential may buy at most 100,000 queries within the 24 hour grant lifetime. Buying past that is refused with `403` naming the time the oldest of those purchases expires, which is when the ceiling drops.

This counts what was bought, not what is left. Spending purchased queries happens inside each proxy and is not reported back, so the control plane cannot know your remaining headroom, only what you paid for recently. An agent that buys the full allowance and spends all of it still waits for those purchases to age out. The ceiling is what bounds the damage if an agent credential is stolen, and it always clears on its own.

## A pack is worth a pack per proxy

Purchased queries are counted in the proxy process that spends them, exactly like the policy's own hourly and daily windows. A grant streamed to several regions is spendable in each of them, and a proxy restart hands back whatever that instance had not spent. This is inherited from how budgets already work rather than something top-ups introduce, and it errs in your favour, but it means a 1,000 query pack is a floor rather than a precise quantity.

## Turning it on

Nothing about payments answers until all of the following are true.

**Control plane** (`PAYMENTS_ENABLED=true`, `PAYMENTS_CHALLENGE_KEY`, `PAYMENTS_API_BASE`, `STRIPE_SECRET_KEY`). The challenge key is at least 32 bytes, hex-encoded, and must be the same key the proxies hold. `PAYMENTS_API_BASE` is the public API origin; its host becomes the challenge realm.

**Data planes** (`PAYMENTS_ENABLED=true`, `PAYMENTS_CHALLENGE_KEY`, `PAYMENTS_API_BASE`), with the same key and the same base URL. Without them the proxy says nothing about payments and a budget refusal reads exactly as it did before.

**Feature flag.** The priced operation is gated on `mpp-payments`. In production and staging the flag provider is required, so an unregistered flag denies with `404`.

**Stripe.** Settlement creates a PaymentIntent with `shared_payment_granted_token`, which needs agentic commerce enabled on the Stripe account. Until it is, settlement fails and the endpoint answers `402` with `verification-failed`.