# Payments

There are three ways to pay. All land in the same ledger: the payment becomes an escrow
hold, the signed verification report decides, and the hold either pays the seller or
goes back to whoever paid.

| Rail | Account | Who funds it | Lands in the ledger as |
| --- | --- | --- | --- |
| Credits | Yes | A person, by card through Stripe | A hold from the principal's credits, under the agent's mandate |
| x402 | No | The agent's own USDC wallet | A hold keyed to the payer wallet |
| MPP session | No | One deposit through a Stripe or Tempo MPP client | One session hold, drawn down by signed vouchers, settled once at close |

Each offer document (`/offers/{id}`) lists the rails that can buy it in its `payment`
block, so an agent can choose before it asks. Credits are always on; x402 and MPP show
only when the operator has switched them on, and never for subscriptions.

## Credits

One credit is 1.00 US dollar. A person buys credits by card
([Pricing](/docs/pricing)), connects an agent, and the agent buys under its mandate with
`purchase_artifact` or `POST /v1/orders` and a Bearer token. Subscriptions are paid from
credits only.

## x402: buy without an account, in USDC

x402 is an open payment protocol over HTTP status 402. An agent with a USDC wallet asks
to buy, gets a quote, signs a transfer authorization, and retries. ContextFile verifies
and settles the transfer through an x402 facilitator and holds the money in escrow like
any other order.

x402 is off until the operator switches it on, and runs on a test network until legal
review of stablecoin payments is done. A listing page says whether it is on.

### 1. Ask to buy, get a 402 quote

Send the purchase with no Authorization header:

```http
POST https://mcp.contextfile.ai/v1/orders
Content-Type: application/json

{ "listing_id": "<listing id>", "formats": ["md"] }
```

The answer is `402 Payment Required` with the x402 payment requirements:

```json
{
  "x402Version": 1,
  "error": "payment_required",
  "accepts": [{
    "scheme": "exact",
    "network": "base-sepolia",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "maxAmountRequired": "4000000",
    "payTo": "0x...",
    "resource": "https://mcp.contextfile.ai/v1/orders",
    "maxTimeoutSeconds": 300,
    "extra": {
      "name": "USDC",
      "version": "2",
      "quote_id": "q1....",
      "quote_expires_at": "2026-11-01T14:07:11.000Z",
      "listing_id": "<listing id>",
      "formats": ["md"],
      "price_cents": 400,
      "escrow": "hold_and_settle",
      "floor": 0.9
    }
  }]
}
```

`maxAmountRequired` is the live price of the basket in USDC base units (6 decimals, so
4000000 is 4 USDC). The figures here are Example values. The price always comes from the
live listing, never a fixed number. The quote expires after `maxTimeoutSeconds`
(Example: 5 minutes).

### 2. Pay with an X-PAYMENT header

Sign an EIP-3009 transfer authorization for exactly `maxAmountRequired` to `payTo`, and
send the same request with the base64 payment payload in `X-PAYMENT`. Send the
`quote_id` back in the body (or in an `X-ContextFile-Quote` header) so the quote's expiry
and price hold:

```http
POST https://mcp.contextfile.ai/v1/orders
Content-Type: application/json
X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLC4uLn0=

{ "listing_id": "<listing id>", "formats": ["md"], "quote_id": "q1...." }
```

A client that does not send the quote id pays the live price at that moment; its
authorization's `validBefore` bounds the wait.

The payment is refused, and nothing is settled, when:

- the quote has expired (`quote_expired`, with a fresh quote in `accepts`),
- the authorization is for more than the maximum (`amount_above_max`) or less than the
  price (`amount_below_price`),
- the price changed since the quote (`price_changed`),
- the facilitator does not verify the signature or the wallet's funds
  (`payment_invalid`).

Every refusal is a 402 with new requirements, so the agent can pay again.

### 3. The payment lands as a hold

```http
HTTP/1.1 201 Created
X-PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4Li4uIn0=

{
  "order_id": "<order id>",
  "state": "held",
  "escrow_cents": 400,
  "payment": { "rail": "x402", "payer": "0x...", "network": "base-sepolia", "tx": "0x..." },
  "order_token": "x4o....",
  "status_url": "https://mcp.contextfile.ai/v1/x402/orders/<order id>",
  "download_url": "https://mcp.contextfile.ai/v1/x402/orders/<order id>/download",
  "settles_on": "report_floor_check"
}
```

Keep `order_token`. It is the wallet's proof of the order: the paid request carried an
authorization only the wallet could sign, and the token is bound to that order and that
payer. Send it as `X-ContextFile-Order-Token` to read the order or download it. Another
wallet's token does not open this order.

### 4. Release or refund

The signed report decides, exactly as for credits:

- **The report meets the floor:** the hold settles. The seller accrues `(T - c) / 2` and
  ContextFile keeps the rest, the same formula as every order. The escrow.release
  receipt carries `payment: { rail: "x402", network, tx }`, the transfer that funded it,
  and is anchored in the daily receipt tree like every receipt.
- **The report misses the floor:** ContextFile sends the whole amount back to the paying
  wallet, and the hold returns to zero. A refund that fails on chain is retried; if
  retries run out it is recorded for manual action and still shows as owed.

```http
GET https://mcp.contextfile.ai/v1/x402/orders/<order id>
X-ContextFile-Order-Token: x4o....
```

answers the state, the payment, any refund (`pending`, `sent` with its transaction, or
`failed`), and the signed receipt once the escrow released.

```http
GET https://mcp.contextfile.ai/v1/x402/orders/<order id>/download
X-ContextFile-Order-Token: x4o....
```

answers the artifact the same way `download_artifact` does, with `artifact_digest` and
`report_digest`. A refunded order no longer downloads.

### Pay per call with x402

A listing with a per-call price answers one question per call from its verified
edition. Send the question to `POST /v1/listings/{listing_id}/calls` with no
Authorization header and you get a 402 whose quote has `extra.quote_kind` set to
`per_call` and the per-call price as the maximum. Retry with `X-PAYMENT` and the
`quote_id`. An answered call settles the transfer, then holds and releases it through
the same escrow as a purchase; the response carries the cited answer, `charged_cents`,
`order_id` and the transfer. A question the edition does not cover answers
`no_answer` and the authorization is never submitted, so nothing is charged. A per-call
order never downloads the artifact. With a Bearer token the same path pays from
credits, capped by the agent's mandate (MCP: `commerce_buy` with `action: "call"`).

### What x402 does not cover

- Subscriptions: a wallet cannot be billed each cycle without a mandate, so living
  artifacts are paid from credits.
- Sellers are paid out as today. USDC comes in; seller payouts do not change.
- Agents that also hold a ContextFile account pay with credits when they send a Bearer
  token. A request with a token is always the credit purchase.

## MPP sessions

Deposit once, pay for many calls, settle once.

MPP (the Machine Payments Protocol from Stripe and Tempo) suits an agent that will make
many paid calls in a row. It deposits once, then pays for each call with a signed
voucher instead of a new payment. ContextFile holds the deposit as one session hold,
turns each voucher into an ordinary escrowed order, and settles the whole session in
one transfer when it closes.

MPP is off until the operator switches it on, and runs on a test network until legal
review is done. A listing page and the offer document say whether it is on.

### 1. Open a session on a deposit

Make the deposit with your MPP client, then open a session with its reference and the
Ed25519 public key that will sign your vouchers (raw 32 bytes, base64url):

```http
POST https://mcp.contextfile.ai/v1/mpp/sessions
Content-Type: application/json

{ "deposit_ref": "0x...", "voucher_key": "<base64url Ed25519 public key>" }
```

ContextFile asks the MPP rail to confirm the deposit, who made it and how much landed,
and answers:

```json
{
  "session_id": "<session id>",
  "session_token": "mps....",
  "state": "open",
  "deposit_cents": 2000,
  "cumulative_cents": 0,
  "expires_at": "2026-11-02T12:00:00.000Z",
  "calls_url": "https://mcp.contextfile.ai/v1/mpp/sessions/<session id>/calls",
  "close_url": "https://mcp.contextfile.ai/v1/mpp/sessions/<session id>/close"
}
```

One deposit opens one session; sending the same open again returns the same session.
The figures are Example values, as are the deposit limits (Example: 1 to 500 credits' worth)
and the session lifetime (Example: 24 hours).

### 2. Pay for each call with a voucher

A voucher states the running total you authorize for the session, in cents, and the
listing this call buys. Sign the exact string

```text
cf-mpp-voucher-v1|<session id>|<cumulative_cents>|<listing id>
```

with your voucher key, and send it with the session token:

```http
POST https://mcp.contextfile.ai/v1/mpp/sessions/<session id>/calls
Content-Type: application/json
X-ContextFile-Session-Token: mps....

{ "listing_id": "<listing id>", "formats": ["md"],
  "voucher": { "cumulative_cents": 400, "signature": "<base64url>" } }
```

The total must rise by exactly the price of this call (the offer document has it). The
call answers `201` with the order, the draw, the new total and what is left of the
deposit. The order is held in escrow and settles on the signed report like any other.

A voucher is refused, and nothing is drawn, when its total does not rise over the last
accepted one (`voucher_not_increasing`, which is what a replayed voucher gets), when it
rises by a different amount than the price (`voucher_amount_mismatch`), when it goes
past the deposit (`voucher_over_deposit`), or when the signature does not verify under
the session's key (`voucher_invalid`). The specific reason is in `mpp_code`.

### 3. Close and settle once

```http
POST https://mcp.contextfile.ai/v1/mpp/sessions/<session id>/close
X-ContextFile-Session-Token: mps....
```

Closing stops new vouchers. While any call's order still waits for its report, the
answer is `202` with `outcome: "waiting"` and the session settles on its own as soon as
the last order settles. Then one settlement runs on the rail:

- calls whose report met the floor are claimed from the deposit. The seller accrues
  `(T - c) / 2` for each, the same formula as every order;
- calls whose report missed the floor, failed-format surcharges, and the part of the
  deposit you never drew go back to you in the same transfer.

`GET /v1/mpp/sessions/<session id>` shows every draw, its order's state and, once
settled, the settlement transaction with what was claimed and what was returned. A
session that reaches its expiry closes the same way. Closing twice changes nothing.

Download what a call bought with
`GET /v1/mpp/sessions/<session id>/orders/<order id>/download` and the session token.

## Catalog channels

The same offer documents feed two catalog formats, rendered on request so a price
change shows in the next response:

- [`/.well-known/ucp`](/.well-known/ucp) is a UCP (Universal Commerce Protocol) business
  profile. It names ContextFile's REST and MCP services, lists the live offers under the
  `ai.contextfile.offers` capability, and lists the payment handlers that are on.
- [`/feeds/acp.json`](/feeds/acp.json) is an ACP (Agentic Commerce Protocol) product
  feed, one product per one-off offer at its live price in cents. It is for discovery:
  OpenAI approves in-app checkout only for physical goods today, so buying happens on
  the rails above.

Both are checked in CI against the published UCP and ACP schemas.

## Receipts and checks

Receipts verify offline against `/.well-known/receipt-keys.json`, and
`/receipts/{id}/proof` shows a receipt's place in its day's anchored tree. The full API
is in the [API reference](/docs/api).



Version 2, updated 2026-11-01.
