> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jojapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pay as an AI agent (x402 / MPP)

> AI agents buy access to any JoJ API without an account: pay per request with a wallet, buy a plan and receive an API key, or fund a pay-as-you-go balance. x402 and MPP over plain HTTP.

An AI agent does not sign up. It pays. Every API on the JoJ API marketplace answers keyless requests with a machine-readable **HTTP 402** offer that an agent wallet can settle in one round trip — over **x402** (USDC on Base) or the **Machine Payments Protocol (MPP)**. The paying wallet becomes the agent's identity: what it buys, it keeps.

## Three ways to buy

| Product | How | What you get |
| - | - | - |
| **Pay per request** | Call a priced endpoint with no API key, receive a `402`, pay, repeat the call | The response. Nothing to store. Price per endpoint is fixed and published |
| **Buy a plan** | `POST https://{api}.jojapi.net/_jojapi/agent/subscribe/{plan}` | A fresh **API key** bound to a prepaid subscription period (quota, rate limit, everything the plan includes) |
| **Fund a balance** | `POST https://{api}.jojapi.net/_jojapi/agent/topup` | Account-wide pay-as-you-go balance, a PAYG subscription on the named API and its API key |

Plan purchases, top-ups and per-request calls all work from the same wallet; the first payment creates the account.

## Discover what is for sale

Every API host publishes its catalog and discovery documents (no auth):

| URL | Content |
| - | - |
| `https://{api}.jojapi.net/_jojapi/agent/plans` | Plans (price, period, quota, rate limit), endpoints sold per request, policies |
| `https://{api}.jojapi.net/openapi.json` | OpenAPI with `x-payment-info` on every payable operation |
| `https://{api}.jojapi.net/.well-known/x402` | The x402 discovery document (resource list + ownership proofs) |
| `https://agents.jojapi.net/apis` | Directory of every agent-payable API on the marketplace |

`/openapi.json`, `/.well-known/x402` and `/favicon.ico` (the API's logo) are reserved on every API host and always answered by the gateway itself, without authentication — also when the provider has not enabled agent payments (then without payment annotations). The `{api}` host is the gateway subdomain shown on the API's page (for `jojapi.com/hub/api/web-search` the calls go to `web-search.jojapi.net`).

**Private APIs are closed to agents.** Their hosts answer `403 api_private` on the catalog and purchase routes, list no payable resources in `/.well-known/x402`, carry no `x-payment-info` in `/openapi.json`, and keyless calls get `401`. Unlisted APIs are purchasable on their host but do not appear in the directory, which lists approved (public) APIs only. An agent that subscribed before an API became private keeps its keys and can renew, like any existing subscriber.

## Pay per request

1. Call the endpoint without a key: `GET https://{api}.jojapi.net/v1/search?q=…`
2. The gateway answers `402 Payment Required` with:
   * `PAYMENT-REQUIRED` — a base64 x402 v2 `PaymentRequired` object (`accepts[]` with the exact USDC amount, network, asset and `payTo`, plus the `bazaar` and `payment-identifier` extensions)
   * one or more `WWW-Authenticate: Payment …` MPP challenges (methods `evm`, and `stripe` / `tempo` where enabled)
   * a JSON body with `reason: "payment_required"`, `price_usd`, and the catalog link
3. Sign the payment with your wallet and repeat the request with `PAYMENT-SIGNATURE: <base64 PaymentPayload>` (x402) or `Authorization: Payment <credential>` (MPP).
4. The gateway verifies the payment with the facilitator, forwards your request to the API, and **settles only if the upstream answered 2xx-4xx**. The response carries `PAYMENT-RESPONSE` (x402) or `Payment-Receipt` (MPP) with the on-chain transaction.

Upstream `5xx` and gateway errors are never charged. If settlement fails after the upstream answered, the body is withheld and a fresh offer is returned (`reason: "settlement_failed"`) — pay again to retry.

<Tabs>
  <Tab title="TypeScript (@x402/fetch)">
    ```ts theme={null}
    import { wrapFetchWithPayment } from "@x402/fetch";
    // signer = any viem/ethers account holding USDC on Base
    const paidFetch = wrapFetchWithPayment(fetch, signer);
    const res = await paidFetch("https://web-search.jojapi.net/v1/search?q=jojapi");
    console.log(res.headers.get("payment-response"), await res.json());
    ```
  </Tab>

  <Tab title="curl (inspect the offer)">
    ```bash theme={null}
    curl -i https://web-search.jojapi.net/v1/search?q=jojapi
    # HTTP/1.1 402 Payment Required
    # payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOi...
    # www-authenticate: Payment id="…", realm="web-search.jojapi.net", method="evm", intent="charge", …
    ```
  </Tab>
</Tabs>

Identity in a paid call is the wallet that signed it. Per-wallet rate limits apply (default 5 requests/second, 300/minute per API); rate-limited requests are never charged.

## Buy a plan and get an API key

```bash theme={null}
curl -i -X POST https://web-search.jojapi.net/_jojapi/agent/subscribe/pro-monthly
# 402 with the plan price in PAYMENT-REQUIRED / WWW-Authenticate
```

Pay the offer (x402 or MPP) and repeat the `POST` with the credential. Include the `payment-identifier` extension (a client-generated id) — it makes the purchase **idempotent**: repeating the request with the same id returns the same result, key included, even if the first response was lost.

The `200` response contains:

```json theme={null}
{
  "status": "success",
  "api_key": "jk_…",
  "auth": { "header": "X-JoJAPI-Key" },
  "subscription": { "ref": "…", "plan": { "slug": "pro-monthly", "price_usd": 9, "period": "1 MONTH", "quota": [ … ] }, "period_end": "2026-10-08 12:00:00", "renewal": "prepaid" },
  "existing": [ … other active subscriptions of this wallet on this API … ],
  "payment": { "reference": "0x…", "rail": "x402" }
}
```

Use the key like any consumer: `X-JoJAPI-Key: jk_…` or `Authorization: Bearer jk_…`. **The key is bound to that subscription.** Buying a second plan of the same API creates a second subscription with its own key — nothing is cancelled; `existing` lists what you already hold. Pass `{"replace": "<subscription ref>"}` in the body to cancel one on purpose (no refund of the remaining period).

Free plans are not available to agents: paid plans, per-request calls and priced pay-as-you-go plans only (a pay-as-you-go plan without a priced tier counts as free). A wallet costs nothing to create, so free plans keep the marketplace's usual abuse protection (one verified human account).

### Renewal

Plans bought by agents are **prepaid periods**; nothing is charged automatically. When the period ends, requests with that key answer `402` with `reason: "renewal_required"` and an offer for the plan price. Pay it against `POST https://{api}.jojapi.net/_jojapi/agent/renew` (send the key too) and a new period starts from now, counters reset. Early renewal is refused with `not_expired`.

## Fund a pay-as-you-go balance

```bash theme={null}
curl -i -X POST https://web-search.jojapi.net/_jojapi/agent/topup \
  -H 'content-type: application/json' \
  -d '{"amount_usd": 25, "subscribe": [{"api": "web-search"}]}'
```

The offer is for exactly `amount_usd`. On payment the balance is credited to the paying wallet's account, a PAYG subscription is created on each API in `subscribe` (its first PAYG plan unless `plan` is given) and the response returns the key for each **newly created** subscription. Later top-ups from the same wallet land on the same account and return only key hints (`…ab12`), never the key again.

A top-up is offered only where the balance can be spent: on API hosts that sell a pay-as-you-go plan (a top-up there opens that API's PAYG subscription by itself, no `subscribe` needed) and on the directory host with `subscribe` naming such APIs. Naming an API without a pay-as-you-go plan answers `409 no_payg_plan` before any payment.

The balance is account-wide: every PAYG key of the account spends it. When it runs out, requests answer `402` with `reason: "insufficient_balance"` and a top-up offer bound to that account — anyone may pay it, the credit goes to the account of the key, and the paying wallet is **not** linked to it.

## Identity, wallets and Sign-In-With-X

* One wallet = one account. The identity key is chain-agnostic on EVM (the same address on Base, Polygon or Arbitrum is one account); Solana addresses are separate identities.
* Nothing is ever merged by guesswork. To use two wallets on one account, link them with a **double signature**: `POST …/_jojapi/agent/identities/link` signed by the existing wallet (`SIGN-IN-WITH-X` header) with the second wallet's proof in the body (`{"link": <siwx proof>}`).
* Identity-only routes use **Sign-In-With-X** (CAIP-122): request the route, receive `402` with `extensions["sign-in-with-x"]`, sign the SIWE / SIWS message, repeat with `SIGN-IN-WITH-X: <base64 proof>`. Nonces are single-use, 5 minutes.

| Route | Purpose |
| - | - |
| `GET …/_jojapi/agent/me` | Account, identities, balance, subscriptions with key hints, recent payments |
| `POST …/_jojapi/agent/keys/rotate` `{"subscription": "<ref>"}` | New key for one subscription; the old key stops working |
| `POST …/_jojapi/agent/identities/link` / `unlink` | Manage wallets |

The same routes exist under `https://agents.jojapi.net/apis/{api-slug}/…`, and account routes without an API under `https://agents.jojapi.net/`.

## Payment rails

| Rail | Header pair | Networks / methods |
| - | - | - |
| **x402 v2** | `PAYMENT-REQUIRED` → `PAYMENT-SIGNATURE` → `PAYMENT-RESPONSE` | `exact` scheme, USDC on Base (`eip155:8453`); more networks as listed in `accepts[]` |
| **MPP** | `WWW-Authenticate: Payment` → `Authorization: Payment` → `Payment-Receipt` | `evm` (EIP-3009 `authorization` credentials, settled through the x402 facilitator); `stripe` (cards) and `tempo` where enabled |

Legacy x402 v1 (`X-PAYMENT`) is not supported.

## Errors

| Status | `reason` | Meaning |
| - | - | - |
| 401 | `api_key_required` | The endpoint is not sold per request; use a key (buy a plan or fund a balance) |
| 402 | `payment_required` | Offer attached; pay and repeat |
| 402 | `payment_invalid` (+ `error`) | The facilitator rejected the payment — `error` carries its code (e.g. `insufficient_funds`, `invalid_payment_requirements`, `duplicate_payment`) |
| 402 | `renewal_required` | Prepaid period ended; pay the renewal offer |
| 402 | `insufficient_balance` | Top-up offer attached |
| 402 | `settlement_failed` | Settlement failed after the upstream answered; body withheld, pay again |
| 402 | `sign_in_required` | Identity route: sign the SIWx challenge |
| 409 | `duplicate_payment` / `payment_in_progress` | The same payment authorization was already used or is being processed |
| 429 | `rate_limited` | Per-wallet or per-IP limit; nothing was charged |

## Refunds

On-chain payments are final. You are never charged for upstream `5xx` or gateway errors; you are charged for `4xx` answers from the API (a wrong parameter is still a served request). Disputes are handled as balance credits on the agent's account — contact the marketplace with the payment reference.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.