---
name: mpp-payments
description: Instructions for AI agents to pay for Tetrac API access using the Machine Payments Protocol (MPP) on Solana. A single universal gateway endpoint charges USDC per request via HTTP 402.
license: Proprietary
compatibility: Designed for @solana/mpp-compatible clients, Claude Code, and AI agents
metadata:
  author: tetrac
  version: "3.3"
  category: "payments"
  emoji: "💳"
---

# MPP Payment Instructions for AI Agents

You can access Tetrac's paid market-data and exchange endpoints (listed under
Available Endpoints) by paying USDC **on Solana** per request via the Machine
Payments Protocol (MPP) — $0.05 for standard endpoints,
$0.50 for the full-market scanner snapshot (`markets/ttc-scanner-cache`). MPP uses
standard HTTP 402 challenges — no custom protocol implementation needed.

## Constants

| Field               | Value                                                                                                                                              |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Base URL            | `https://www.tetrac.xyz`                                                                                                                           |
| Gateway             | `POST /api/v1/mpp` (GET also supported)                                                                                                            |
| Price               | `50000` = $0.05 for most endpoints; `500000` = $0.50 for `markets/ttc-scanner-cache` (6-decimal base units). Exact amount is in the 402 challenge. |
| Chain               | Solana                                                                                                                                             |
| USDC Mint (mainnet) | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`                                                                                                     |

> **Note:** The amount, recipient, and network are returned dynamically in the 402
> challenge's `WWW-Authenticate: Payment` header (the base64url `request` parameter);
> the currency is labeled `USDC`. Always use the values from the challenge — do not
> hardcode.

## Payment Flow

```
POST /api/v1/mpp → 402 Challenge → Client signs & pays USDC on Solana → Retry with credential → 200 + Data + Receipt
```

### Step 1: Send a request to the gateway

POST to `/api/v1/mpp` with a JSON body specifying which endpoint you want:

```json
{
  "method": "GET",
  "path": "markets/hybrid-tickers",
  "parameters": "symbol=BTCUSDT"
}
```

You will receive a `402 Payment Required` response. The Solana USDC charge terms
are in the `WWW-Authenticate` header; the JSON body is an RFC 9457 problem
document (`{ type, title, status, detail, challengeId }`).

### Step 2: Pay the challenge

The 402 response contains the payment instructions. A `@solana/mpp`-compatible
client handles this automatically. If building a custom client, sign a USDC
transfer transaction on the specified Solana network and attach the payment
credential to your retry.

> **Pull mode only.** Send the signed transaction itself — credential payload
> `{ "type": "transaction", "transaction": "<base64 wire transaction>" }`, with
> your wallet as the fee payer — and the gateway simulates and broadcasts it.
> This is the `@solana/mpp` client default (`solana.charge({ signer })`).
> **Push mode is not accepted:** a `{ "type": "signature" }` credential (the
> client's `broadcast: true` option) is refused with a fresh `402`, and because a
> push-mode client broadcasts before it sends the credential, that transfer is
> already spent. Never set `broadcast: true`. A transaction that has already
> landed, or that was already presented to the gateway, is refused the same way:
> sign a new one for the new challenge.

### Step 3: Retry with payment credential

Replay the same request with the payment credential attached. The gateway
verifies the on-chain payment, forwards your request to the upstream
`/api/v1/*` endpoint, and returns the data plus a `Payment-Receipt` header and a
Solana explorer link.

> **The gateway validates your request before it charges.** A missing or
> malformed `path`, a bad `method`, or an endpoint outside the table below is
> refused with `400` and no payment challenge, so it costs nothing (so are some
> invalid `parameters` / `body` values the endpoint is known to refuse, for
> example a scanner query without `symbol`). An error from the upstream endpoint
> itself is reported only **after** payment settles, as a `502` — or a `400` if
> the endpoint is not found — that still carries the `Payment-Receipt` header and
> the settlement `transaction`, your proof of payment. Payments are not refunded.
> Check `parameters` against this document before paying.

## Request Body Format

| Field        | Type   | Required | Description                                    |
| ------------ | ------ | -------- | ---------------------------------------------- |
| `method`     | string | No       | `"GET"` (default) or `"POST"`                  |
| `path`       | string | Yes      | Endpoint path, e.g. `"markets/hybrid-tickers"` |
| `parameters` | string | No       | Query string: `"symbol=BTCUSDT"`               |
| `body`       | object | No       | Request body for POST upstream calls           |
| `ref`        | string | No       | Referrer: a TTC user's Solana wallet (base58)  |

### Referrals (`ref`)

Pass `ref` (in the JSON body, or `&ref=` on a GET) to credit a TTC user who referred
you. When `ref` is a valid wallet that belongs to a TTC user (and isn't your own
signed-in key), the 402 challenge carries a split: **20% of the price goes to the
referrer in the same transaction**, and the treasury receives the rest. **You pay the
same total either way.** The challenge's `currency` is then the USDC mint address
instead of the `"USDC"` label, and the split may ask your wallet to create the
referrer's USDC account first (a one-time ~0.002 SOL rent, as for any new account).

Send the **same `ref`** on the paid retry: the challenge is bound to its split. A
`ref` that isn't eligible is ignored, and the charge goes 100% to the treasury.

### Path format

`path` may be the full form (`api/v1/markets/news`) or a short form that starts
with `markets/` or `exchanges/` (`markets/news`, `exchanges/latency`). Bare
`exchanges` is **not** a valid short form — use `api/v1/exchanges` — and
`solana-perps/*` endpoints need the full form too. A leading `/` is tolerated,
an inline `?query` is dropped (pass it in `parameters`), and `..` is stripped.
The table below is the gateway's complete allowlist: any other path — including
other `/api/v1/*` routes — is refused with `400` before a challenge is issued.

## Available Endpoints

| Path                                     | Method | Data                                                              |
| ---------------------------------------- | ------ | ----------------------------------------------------------------- |
| `markets/ttc-scanner`                    | GET    | Per-symbol technical scanner                                      |
| `markets/ttc-scanner-cache`              | GET    | Full-market signal snapshot ($0.50) — send `parameters: "full=1"` |
| `markets/hybrid-tickers`                 | GET    | Aggregated perpetual futures tickers                              |
| `markets/funding-rates`                  | GET    | Perpetual funding rates                                           |
| `markets/open-interest`                  | GET    | Open interest from one venue per call                             |
| `markets/volume-snapshot`                | GET    | Per-exchange volume snapshots                                     |
| `markets/swap-volume`                    | GET    | Perp DEX daily volume history                                     |
| `markets/symbol-venues`                  | GET    | Which venues list each symbol                                     |
| `markets/listings`                       | GET    | New market listings                                               |
| `markets/insights`                       | GET    | Top movers with funding-rate context                              |
| `markets/news`                           | GET    | Market news (Alpha Vantage)                                       |
| `markets/calendar`                       | GET    | Macro economic calendar                                           |
| `markets/quakes`                         | GET    | Significant earthquakes (USGS)                                    |
| `api/v1/solana-perps/long-short`         | GET    | Solana perp long/short open interest                              |
| `api/v1/solana-perps/pacifica-watchlist` | GET    | Top Pacifica trader accounts by open interest                     |
| `api/v1/exchanges`                       | GET    | Methods catalog and supported exchanges                           |
| `api/v1/exchanges`                       | POST   | Unified Trading API                                               |
| `exchanges/latency`                      | GET    | Exchange latency                                                  |

Without `full=1`, `markets/ttc-scanner-cache` returns only freshness timestamps and
the $0.50 is still charged. Parameters and response shapes for each endpoint are
in the [market-data](https://www.tetrac.xyz/skills/market-data/SKILL.md) and
[trading](https://www.tetrac.xyz/skills/trading/SKILL.md) skills.

## Examples

### Using `@solana/mpp` (client)

```ts
import { Mppx, solana } from "@solana/mpp/client";

// `signer` is any Solana TransactionSigner (e.g. createKeyPairSignerFromBytes
// from @solana/kit, a wallet-adapter signer, or a remote Keychain signer).
const mppx = Mppx.create({ methods: [solana.charge({ signer })] });

// mppx.fetch handles the 402 automatically: signs + pays USDC, then retries.
const res = await mppx.fetch(
  "https://www.tetrac.xyz/api/v1/mpp?path=markets/hybrid-tickers&parameters=symbol%3DBTCUSDT",
);
const data = await res.json();
```

### Inspecting the 402 challenge with curl

```bash
# Returns 402 with the Solana USDC challenge — useful to inspect the format.
curl -s -X POST https://www.tetrac.xyz/api/v1/mpp \
  -H "Content-Type: application/json" \
  -d '{"method":"GET","path":"markets/news"}' -i
```

## Response Format

A successful response is the upstream endpoint's JSON body, unchanged, with a
`transaction` field added for the settlement:

```json
{
  "success": true,
  "timestamp": 1774435610722,
  "data": { "...": "..." },
  "transaction": {
    "hash": "5xExAmpLeSoLaNaSiGnAtUrE...",
    "explorer": "https://explorer.solana.com/tx/5xExAmpLeSoLaNaSiGnAtUrE..."
  }
}
```

The fields other than `transaction` depend on the endpoint — some bodies have
no `data` wrapper (see the market-data skill).

The `Payment-Receipt` header is also set on the response for programmatic
verification.

If the upstream endpoint fails after your payment settles, the body says why
and still carries the settlement:

```json
{
  "error": "Upstream endpoint failed after payment",
  "upstreamStatus": 400,
  "upstream": { "success": false, "error": "..." },
  "transaction": {
    "hash": "5xExAmpLeSoLaNaSiGnAtUrE...",
    "explorer": "https://explorer.solana.com/tx/5xExAmpLeSoLaNaSiGnAtUrE..."
  }
}
```

## Error Handling

| Status | Charged | Meaning                                                                                                                                                                          | Action                                                               |
| ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `400`  | No      | Missing or malformed `path`, bad `method`, an endpoint outside the table, or `parameters` / `body` refused up front (no challenge)                                               | Fix the request against the endpoints table                          |
| `402`  | No\*    | Payment required — or the credential was refused (push mode, an already-landed or already-presented transaction, an expired challenge, failed verification); `detail` says which | Sign a new pull-mode transaction for the challenge and retry         |
| `429`  | No      | Gateway busy                                                                                                                                                                     | Retry after `Retry-After` seconds                                    |
| `503`  | No      | Payment verification temporarily unavailable                                                                                                                                     | Retry after `Retry-After` seconds                                    |
| `200`  | Yes     | Success                                                                                                                                                                          | Parse the response body as JSON                                      |
| `502`  | Yes     | The upstream endpoint failed, or did not answer JSON, after payment; `upstreamStatus` / `upstream` say why. Has `Payment-Receipt`                                                | Keep the receipt and `transaction`; the payment is not refunded      |
| `400`  | Yes     | The upstream endpoint was not found after payment. Has `Payment-Receipt`                                                                                                         | Keep the receipt and `transaction`; check the path against the table |

\* A push-mode client has already spent its transfer when it is refused (see Step 2).
In the rare case that confirmation timed out, your pull-mode transaction may still
land: check it on the explorer before paying again.

**Rate limit.** MPP calls share one gateway-wide ceiling of 600 forwarded calls per
minute across all MPP callers. When it is full, the gateway answers `429` with
`Retry-After` **before** it charges you, so a busy gateway never costs a payment.

## Specification References

- MPP Protocol: https://mpp.dev
- Solana Charge Draft: https://paymentauth.org/draft-solana-charge-00.txt
- @solana/mpp SDK: https://www.npmjs.com/package/@solana/mpp
- mppx SDK: https://www.npmjs.com/package/mppx
