# Pony > Transactional email with an x402 purchase path. An agent with a funded wallet > can buy a plan and send its first email without a human, a card, a browser, or > a DNS record. Base URL: `https://api.pony.email` Auth: `Authorization: Bearer ` ## There is no SDK, and that is deliberate We publish no client libraries. This file, and the provider-shaped bundles below, are the integration. Read one and write the client yourself, in whatever language you are already working in — the API is REST and JSON, and a wrapper would only serve the languages we happened to choose. | Runtime | How to load it | |---|---| | Claude | `claude skill add pony.email/skill` | | OpenAI | `GET /skills/openai.json` — function definitions for `tools[]` | | Gemini | `GET /skills/gemini.json` — function declarations | | Any MCP client | `https://api.pony.email/mcp` — see [MCP](#mcp) for the header it requires | | Everything else | this file | If a provider bundle disagrees with this file, this file is right. The bundles track vendor formats and will lag; this one is updated first. ## Buying a plan with x402 The purchase path is x402 version 2. Header names are `PAYMENT-REQUIRED` (server to client, base64), `PAYMENT-SIGNATURE` (client to server, base64) and `PAYMENT-RESPONSE` (server to client, base64). A legacy v1 `X-PAYMENT` header is also accepted. 1. `POST /emails` with no credentials. The response is `402 Payment Required` with a `PAYMENT-REQUIRED` header. 2. Base64-decode that header. `resource.url` is absolute, and on a refusal it names the catalogue, `https://api.pony.email/plans`. `accepts[]` carries one entry per purchasable plan, each naming `scheme` (`exact`), `network` (CAIP-2, `eip155:8453` for Base), `amount` (USDC atomic units, 6 decimals), `asset`, `payTo` and `maxTimeoutSeconds`. An entry identifies its plan by `amount` and not by name, so match it against `GET /plans`. 3. Sign an EIP-3009 `transferWithAuthorization` for exactly that amount, to exactly that `payTo`. 4. `POST /plans/{plan}/purchase` for the plan you chose, with the base64 payload in a `PAYMENT-SIGNATURE` header. Sending that header anywhere else does nothing: the purchase route is the only one that reads it. The response carries `api_key`, `plan`, `sends_included`, `expires_at`, `sending_address` and `transaction`. 5. Retry the original send with `Authorization: Bearer `. Payment is idempotent on `(payer, nonce)`. Replaying the same signed authorization returns the original result, including the API key, rather than charging again. Settlement is handled by a facilitator. Pony never holds a key, never verifies a signature, and cannot alter the amount or the recipient — both are inside what you signed. ## Plans Every plan is a 30-day entitlement carrying a fixed number of sends. It does not auto-renew and there is no overage: an exhausted or expired plan answers `402` with a fresh quote, which you can pay to continue. | plan_id | Sends per 30 days | Daily cap | Price | |---|---|---|---| | `free` | 3,000 | 100 | not sold, and not granted at launch | | `pro` | 50,000 | none | 19.00 USDC | | `scale` | 100,000 | none | 79.00 USDC | `GET /plans` returns this table as JSON. It is unauthenticated. The free row is listed with a null price and cannot be obtained: an account comes into existence only when a purchase settles, so `pro` and `scale` are the whole catalogue you can act on. ## Sending - `POST /emails` — `from`, `to`, `subject`, and one of `html` or `text`. `to`, `cc`, `bcc` and `reply_to` accept a string or an array. Up to 50 recipients across `to`, `cc` and `bcc`. - `POST /emails/batch` — up to 100 messages. - `GET /emails/{id}` — status, attempts, last error, last event. - `GET /emails` — `limit`, `status`, `to`, `subject`, `before`, `since`. - `GET /emails/{id}/events` — the append-only event log. - `GET /usage` — sends spent against the current entitlement. Send `Idempotency-Key` on any send you might retry. It is honoured for 24 hours; reusing a key with different content is a `409`. ## Sending identity A purchase provisions a verified sending identity at `agent@t-.agents.pony.email` and returns it as `sending_address`. `` is the whole `team_id` from that response — a UUID, not a prefix of one — so the host is `t-` followed by it. Use the address as returned rather than rebuilding it. The first send needs no DNS. The identity carries a volume ceiling. For real volume, add a domain you own with `POST /domains` and publish the four records it returns. ## MCP `https://api.pony.email/mcp` mounts this API as MCP tools. It is the streamable HTTP transport in **stateless mode with JSON responses**: every POST is answered with one `application/json` body, there is no SSE stream, and no `Mcp-Session-Id` is issued or expected. `GET` and `DELETE` answer `405` with an `Allow` header. That is the specification's stateless mode rather than a missing feature — the server-push channel would carry nothing this API sends. **Send `Accept: application/json, text/event-stream` on every POST.** Both types, even though only JSON ever comes back. The specification requires it and the transport enforces it: one type alone is a `400`. This is the first thing a hand-written client gets wrong. **Authentication is the `Authorization: Bearer ` header and nothing else** — not the URL, not a query parameter, not a tool argument. A credential in a path or a query string ends up in access logs and proxy caches, so there is one mechanism rather than a chain of them. **A client that cannot attach a custom header can call `get_plans` and `buy_plan`, and is refused on the other three.** Check that before you connect. | Tool | API key | What it does | |---|---|---| | `get_plans` | not needed | Every plan, with the full x402 terms for each purchasable one: `scheme`, `network`, `asset`, `payTo`, `amount`, `maxTimeoutSeconds`. The same terms a 402 carries, so a caller never has to trigger a refusal to learn the price | | `buy_plan` | not needed | Buys a plan with a signed x402 payment and returns an API key. Called with no signature, it returns that plan's terms. Presenting a key renews the team that holds it instead of creating a second one. Idempotent on `(payer, nonce)` | | `send_email` | required | Sends one email and returns the message id. An exhausted or expired plan fails with `payment_required`, carrying the terms to buy another | | `get_email` | required | One message's delivery record: status, attempts, last error, last event. The body is deliberately not returned — this answers where a message got to, not what it said | | `get_usage` | required | Sends spent against the current window, and when that window ends. A team with no active plan gets `payment_required` with a quote rather than a body of zeroes | The two unauthenticated tools are the pair that takes a caller from nothing to a key: call `get_plans`, sign an EIP-3009 `transferWithAuthorization` for the plan you want, then call `buy_plan` with the base64 payload. A failed tool call returns `isError` with structured content shaped `{"error": {"statusCode", "message", "name"}}`, using the same names as the HTTP errors below. Branch on `name`, never on the message text. A `payment_required` also carries `error.payment_required`, the x402 document naming exactly what to pay. Pay it and call again — nothing refills on its own, so waiting never resolves one. ## Errors Every error body is `{"statusCode", "message", "name"}`. Branch on `name`, never on the message text. Notable names: `missing_api_key` (401), `invalid_api_key` (403), `validation_error` (400 or 422), `missing_required_field` (422), `invalid_idempotent_request` (409), `daily_quota_exceeded` (429), `payment_required` (402). A `402` always carries a `PAYMENT-REQUIRED` header. It means the refusal is one you can fix by paying, and the header says exactly what to pay. A `429` carries `Ratelimit-Limit`, `Ratelimit-Remaining`, `Ratelimit-Reset` and `Retry-After`. All are whole seconds, not epochs. ## Webhooks `POST /webhooks` registers an endpoint and returns a signing secret once. Payloads are signed `Pony-Signature: t=,v1=`. Events are `delivered`, `bounced`, `complained`, `opened` and `rejected`. Delivery is at-least-once with a dead-letter queue; dedupe on `Pony-Delivery-Id`. ## Not supported Attachments, open and click tracking, templates, contact lists, and audiences. This is a transactional send API only.