> ## Documentation Index
> Fetch the complete documentation index at: https://paperplane-justin-winter-s-projects.vercel.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent payments (x402)

> Let a funded agent wallet pay for postage directly — no human checkout, no card.

Every other payment path on paperplane ends at a Stripe-hosted `payment_url` a
**human** has to open. That's correct for a human-approved send and a dead end
for a funded autonomous agent — an AWS Bedrock AgentCore payment, an x402-wallet
bot, or an agent marketplace buyer — that transacts machine-to-machine with no
person in the loop.

`POST /v1/x402/credits` (and the `buy_credits` MCP tool) is that path: an agent
pays USDC on Base directly from its own wallet, using the
[x402](https://github.com/coinbase/x402) protocol's plain HTTP-402
challenge/response handshake, and receives a prepaid paperplane credit code it
then spends with `send_letter` or `POST /v1/orders`.

<Note>
  **Status: staged, not live.** The full x402 v2 handshake — challenge, verify,
  settle, mint — is real, tested code. It reports `x402_not_configured` (HTTP
  501\) on every deployment today because going live needs two provisioning
  steps that are deliberately out of scope for this build: a Coinbase
  Developer Platform (CDP) facilitator account, and enabling "Stablecoins and
  Crypto" as a payment method on paperplane's Stripe account. `sandbox: true`
  works today regardless — see below.
</Note>

## Why credits, not a direct paid send

x402 sells **credits**, not a one-shot paid letter. A credit is refundable by
policy even though on-chain settlement itself is final — see
[Refunds](#refunds) below — and reuses the exact redemption, expiry, and
ledger logic every other credit purchase already goes through (see
[Credits](/docs/guides/credits)). Buying credits over x402 and then spending them
with `send_letter`'s `credit_code` is two well-tested capabilities composed,
not a new one.

## The flow

<Steps>
  <Step title="1. Ask for a price">
    Call `POST /v1/x402/credits` (or `buy_credits`) with `{ pack, email }` and no
    payment. The response is HTTP 402 with a `payment_required` code and a full
    x402 payment-requirements challenge: network, amount, the `payTo` address, asset.
  </Step>

  <Step title="2. Sign and pay">
    Your wallet signs the challenge (x402 v2 "exact" scheme — typically a
    gasless EIP-3009 `transferWithAuthorization` on Base USDC) and produces a
    payment payload.
  </Step>

  <Step title="3. Settle">
    Call the same endpoint again with the signed payload in the `X-PAYMENT`
    header (REST) or `payment_header` (MCP). The server verifies and settles
    against the configured CDP facilitator, then mints a credit code.
  </Step>

  <Step title="4. Spend it">
    Pass the returned `credit_code` to `send_letter` or `POST /v1/orders`. The
    letter moves immediately — no further payment step, human or agent.
  </Step>
</Steps>

```json 402 Payment Required theme={null}
{
  "x402Version": 1,
  "code": "payment_required",
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",
    "maxAmountRequired": "9.99",
    "resource": "https://sendpaperplane.com/v1/x402/credits",
    "payTo": "0x...",
    "asset": "USDC"
  }]
}
```

```json 201 Created — settled theme={null}
{
  "status": "ok",
  "credit_code": "pp-a1b2-c3d4-e5f6",
  "face_cents": 999,
  "bonus_cents": 250,
  "settlement": { "network": "eip155:8453", "tx_hash": "0xfeed..." }
}
```

A repeated call with the **same signed payload** (a client retry after a
dropped response) replays the original `credit_code` with `replayed: true`
rather than minting a second one — the settlement's transaction hash is the
idempotency key.

## Sandbox: works today, no provisioning needed

Pass `sandbox: true` and the endpoint mints a real, spendable credit code
immediately — no wallet, no facilitator call, no charge — **even while x402
itself is disabled** on the deployment. That is deliberate: the whole
agent-payments lane has to be integration-testable for free before any CDP
account or Stripe toggle exists, the same promise `sandbox: true` already
makes on `POST /v1/orders`.

```bash theme={null}
curl -X POST https://sendpaperplane.com/v1/x402/credits \
  -H 'Content-Type: application/json' \
  -d '{ "pack": "starter", "email": "agent@example.com", "sandbox": true }'
```

## Refunds

x402 settlement is **final on-chain** — there is no chargeback and no native
refund mechanism, unlike a card payment. That does not make the purchase
unrefundable:

* An **unredeemed credit is refundable by policy**, same as any other credit
  pack — issued back as paperplane credit, or on request as a voluntary USDC
  return.
* **Undeliverable-mail guarantees still pay out**, always in credit (never a
  reversed on-chain transfer, which is not possible).

Publishing this plainly, here, before any real settlement happens is itself a
requirement of the design — see `docs/PRD-agentic-payments.md`.

## Geography

Stripe does not offer the "Stablecoins and Crypto" payment method in every
region — **New York is excluded today**. The endpoint hides the lane there:
a request from an unsupported region gets `region_unsupported` (HTTP 403)
instead of a payment challenge, pointing back at `POST /v1/credits/checkout`
or a prepaid `credit_code`. This is Stripe's exclusion, not a paperplane
policy choice.

## `buy_credits` (MCP)

Same handshake, over MCP, for an agent that holds a wallet directly:

```json theme={null}
// First call — no payment_header
{ "pack": "starter", "email": "agent@example.com" }
```

```json theme={null}
// Response: sign challenge.accepts[0], then call again with payment_header
{ "status": "action_required", "code": "payment_required", "challenge": { "...": "..." } }
```

```json theme={null}
// Second call
{ "pack": "starter", "email": "agent@example.com", "payment_header": "<signed payload>" }
```

See the [MCP guide](/docs/guides/mcp) for the other three tools and the
quote → confirm → send discipline `buy_credits` sits alongside.

## Errors

`x402_not_configured`, `payment_required`, `x402_payment_invalid`,
`x402_verify_failed`, `x402_settle_failed`, and `region_unsupported` are all in
the [error contract](/docs/guides/errors#payment-errors) with the same `{ status,
code, reason, next }` shape every other paperplane failure uses.
