Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Dead Simple Email Inboxes API
version: 0.1.0
description: 'Email API for AI agents. Create inboxes, send and receive email,
manage threads, and get real-time webhook notifications.
## Authentication
All authenticated endpoints use Bearer token auth:
```
Authorization: Bearer dse_your_api_key
```
## Agent self-signup
Autonomous agents can provision an account, a key, and a live inbox in one
unauthenticated call — no human, no verification email:
```
POST /v1/auth/agent-signup
```
That returns a `trial` account (1 inbox, 10 sends/hour, 25 sends/day) with a
scoped key. `POST /v1/auth/claim` attaches a verified email and moves it to
the Free plan, keeping the same key, inboxes and history.
## Sign in with Dead Simple (OpenID Connect)
`https://id.deadsimple.email` is an OpenID Connect provider where the
identity is an agent''s inbox: `sub` is the `inbox_id`, `email` is the inbox
address. Discovery at `/.well-known/openid-configuration`, JWKS at
`/.well-known/jwks.json`, ES256 id_tokens, authorization-code + PKCE S256,
RFC 7591 registration at `/register`. Open clients (client_id = the app''s
https origin) need no registration. An agent authenticates at `/authorize`
with `Authorization: Bearer dse_...` plus `inbox_id=`, or with an ES256
assertion signed by a key enrolled via `/v1/inboxes/{inbox_id}/identity/keys`;
the one-call helper `POST /v1/inboxes/{inbox_id}/identity/sign-in` wraps
this for SDKs and the MCP server. Endpoints on the `id.deadsimple.email`
host are tagged **Identity Provider** below and carry their own `servers`
entry.
## Pay per request (no API key)
Twelve priced routes can be called with no credentials at all. They answer
`402 Payment Required` carrying an x402 v2 `PAYMENT-REQUIRED` header (USDC
on Base, Polygon or Solana, `exact` scheme, PayAI facilitator; the same
JSON is repeated in the body for v1 clients) and, on `POST /v1/inboxes`,
a `WWW-Authenticate: Payment` challenge for Stripe''s Machine Payments
Protocol. Retry with `PAYMENT-SIGNATURE` (x402 v2), `X-PAYMENT` (v1) or
`Authorization: Payment ` (MPP). x402 payments are verified
before the work and settled after it; MPP charges first and refunds if
the work fails. Prices: inbox $1.00, send/reply/reply-all/forward $0.01,
reads $0.005. The paying wallet keys a `payg` account, and the first paid
inbox returns `credentials` (an API key) in the response so later reads
and sends within the plan are free. See the reusable `402` response and
https://deadsimple.email/pay-per-request.html.
## Idempotency
POST endpoints accept an `Idempotency-Key` header. Same key within 24h
returns the cached response without re-executing.
## Rate Limits
Every response includes rate limit headers:
- `X-RateLimit-Limit` — requests per window
- `X-RateLimit-Remaining` — remaining requests
- `X-RateLimit-Reset` — window reset timestamp
On `429`, a `Retry-After` header gives the seconds to wait. Per-account
limits scale with plan (`rate_limit_rpm`); public/unauthenticated endpoints
are additionally limited per IP.
## Webhook Reliability
Every event is a durable delivery: the first attempt runs immediately and
failures are retried on an exponential-backoff schedule (configurable per
webhook via `/v1/webhooks/{id}/config`; default `[1, 30, 300, 1800, 7200,
86400]` seconds). Deliveries are HMAC-SHA256 signed on **all plans**. The
per-webhook delivery log (`/v1/webhooks/{id}/deliveries`) records every
attempt with status and error, and any delivery can be replayed. Endpoints
that fail repeatedly are auto-disabled.
## WebSocket Streaming
Connect to `wss://ws.deadsimple.email?api_key=dse_...` for real-time events.
After connecting, subscribe to specific inboxes/events:
```json
{"action": "subscribe", "inbox_ids": ["..."], "events": ["message.received"]}
```
Events are pushed as JSON with the same payload as webhooks. Send `{"action": "ping"}`
to keep the connection alive (10-minute idle timeout).'
contact:
url: https://deadsimple.email
license:
name: MIT
servers:
- url: https://api.deadsimple.email
description: Production
security:
- BearerAuth: []
tags:
- name: Inboxes
paths:
/v1/inboxes:
post:
summary: Create inbox
tags:
- Inboxes
parameters:
- $ref: '#/components/parameters/WorkspaceHeader'
requestBody:
content:
application/json:
schema:
type: object
properties:
display_name:
type: string
domain:
type: string
default: deadsimple.email
local_part:
type: string
tags:
type: array
items:
type: string
example:
display_name: Support Bot
tags:
- support
- bot
responses:
'201':
description: Inbox created with email address. When paid per request, the response also carries a payment header and, on a payer's first inbox, `credentials`.
headers:
PAYMENT-RESPONSE:
description: x402 settlement, base64 JSON {success, transaction, network, payer}. Also sent as X-PAYMENT-RESPONSE.
schema:
type: string
X-PAYMENT-RESPONSE:
description: Same value as PAYMENT-RESPONSE, for x402 v1 clients.
schema:
type: string
Payment-Receipt:
description: Stripe MPP receipt, base64url JCS JSON {method "stripe", reference "pi_...", status "success", timestamp, amount (cents), currency}.
schema:
type: string
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/InboxCreated'
'402':
$ref: '#/components/responses/PaymentRequired'
operationId: postV1Inboxes
x-operation-id-source: derived
get:
summary: List inboxes
tags:
- Inboxes
parameters:
- $ref: '#/components/parameters/WorkspaceHeader'
- name: workspace_id
in: query
required: false
schema:
type: string
format: uuid
description: Filter inboxes by workspace
- name: domain
in: query
schema:
type: string
- name: status
in: query
schema:
type: string
enum:
- active
- paused
- name: tag
in: query
schema:
type: string
- name: limit
in: query
schema:
type: integer
default: 50
- name: cursor
in: query
schema:
type: string
responses:
'402':
$ref: '#/components/responses/PaymentRequired'
'200':
description: Paginated list of inboxes
operationId: getV1Inboxes
x-operation-id-source: derived
/v1/inboxes/bulk:
post:
summary: Bulk create inboxes
description: Create up to 100 inboxes in one call. Returns a per-inbox result.
tags:
- Inboxes
parameters:
- $ref: '#/components/parameters/WorkspaceHeader'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- inboxes
properties:
inboxes:
type: array
items:
type: object
properties:
display_name:
type: string
local_part:
type: string
domain:
type: string
tags:
type: array
items:
type: string
responses:
'200':
description: Per-inbox creation results with created/failed/total counts
operationId: postV1InboxesBulk
x-operation-id-source: derived
delete:
summary: Bulk delete inboxes
description: Delete up to 100 inboxes in one call. Returns a per-inbox result.
tags:
- Inboxes
parameters:
- $ref: '#/components/parameters/WorkspaceHeader'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- inbox_ids
properties:
inbox_ids:
type: array
items:
type: string
example:
inbox_ids:
- inbox_abc
- inbox_def
responses:
'200':
description: Per-inbox deletion results with deleted/failed/total counts
operationId: deleteV1InboxesBulk
x-operation-id-source: derived
/v1/inboxes/{inbox_id}:
get:
summary: Get inbox
tags:
- Inboxes
parameters:
- name: inbox_id
in: path
required: true
schema:
type: string
- $ref: '#/components/parameters/WorkspaceHeader'
responses:
'402':
$ref: '#/components/responses/PaymentRequired'
'200':
description: Inbox details
operationId: getV1InboxesByInboxId
x-operation-id-source: derived
patch:
summary: Update inbox
tags:
- Inboxes
parameters:
- name: inbox_id
in: path
required: true
schema:
type: string
- $ref: '#/components/parameters/WorkspaceHeader'
requestBody:
content:
application/json:
schema:
type: object
properties:
display_name:
type: string
tags:
type: array
items:
type: string
status:
type: string
enum:
- active
- paused
responses:
'200':
description: Updated inbox
operationId: patchV1InboxesByInboxId
x-operation-id-source: derived
delete:
summary: Delete inbox
tags:
- Inboxes
parameters:
- name: inbox_id
in: path
required: true
schema:
type: string
- $ref: '#/components/parameters/WorkspaceHeader'
responses:
'204':
description: Inbox deleted and mailbox deprovisioned
operationId: deleteV1InboxesByInboxId
x-operation-id-source: derived
/v1/inboxes/{inbox_id}/credentials:
get:
summary: Get IMAP/SMTP credentials for an inbox
description: Raw mailbox credentials for connecting standard mail clients.
tags:
- Inboxes
parameters:
- name: inbox_id
in: path
required: true
schema:
type: string
responses:
'200':
description: IMAP/SMTP host, port, username, password
operationId: getV1InboxesByInboxIdCredentials
x-operation-id-source: derived
components:
schemas:
Inbox:
type: object
properties:
inbox_id:
type: string
format: uuid
workspace_id:
type: string
format: uuid
email:
type: string
format: email
local_part:
type: string
domain:
type: string
display_name:
type: string
tags:
type: array
items:
type: string
status:
type: string
enum:
- active
- paused
message_count:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
InboxCreated:
allOf:
- $ref: '#/components/schemas/Inbox'
- type: object
properties:
credentials:
$ref: '#/components/schemas/PaymentCredentials'
payment:
$ref: '#/components/schemas/PaymentSettlement'
PaymentSettlement:
type: object
description: How the call was paid. x402 gives {transaction, network, payer}; MPP gives {reference, method}.
properties:
transaction:
type: string
description: On-chain transaction (x402).
network:
type: string
description: CAIP-2 network (x402).
payer:
type: string
description: Payer wallet address (x402).
reference:
type: string
description: Stripe PaymentIntent id (MPP).
method:
type: string
example: stripe
description: Payment method (MPP).
PaymentCredentials:
type: object
description: Issued once, on a payer's first paid inbox. The API key belongs to the Pay as you go account keyed on the wallet (or created for this card purchase) and has the same scoped permissions as an agent-signup key.
properties:
api_key:
type: string
description: Shown once.
key_id:
type: string
account_id:
type: string
note:
type: string
warning:
type: string
PaymentRequiredBody:
type: object
description: Our standard error envelope plus the x402 v2 fields (minus its own error string) so that x402 v1 clients, which read requirements from the body, can pay too.
properties:
error:
type: object
properties:
code:
type: string
example: payment_required
message:
type: string
price_usd:
type: string
example: '1.00'
docs:
type: string
example: https://deadsimple.email/pay-per-request.html
reason:
type: string
description: Present when a submitted payment was rejected (amount mismatch, network not accepted, facilitator invalidReason, declined card, challenge reused).
meta:
type: object
properties:
request_id:
type: string
x402Version:
type: integer
example: 2
resource:
type: object
properties:
url:
type: string
description:
type: string
mimeType:
type: string
accepts:
type: array
items:
$ref: '#/components/schemas/X402Requirement'
extensions:
type: object
X402Requirement:
type: object
description: One accepted way to pay, from the x402 v2 accepts array.
properties:
scheme:
type: string
example: exact
network:
type: string
description: CAIP-2 id. eip155:8453 (Base), eip155:137 (Polygon) or solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp.
amount:
type: string
description: Atomic USDC, six decimals. "1000000" is $1.00.
asset:
type: string
description: USDC contract (EVM) or mint (Solana).
payTo:
type: string
maxTimeoutSeconds:
type: integer
example: 300
extra:
type: object
description: EIP-712 domain {name, version} on EVM networks; {feePayer} on Solana.
responses:
PaymentRequired:
description: Payment required. The request had no API key and this route can be paid per call. The x402 requirements are in the PAYMENT-REQUIRED header and repeated in the body; routes priced at or above $0.50 (inbox creation) also carry a Stripe MPP challenge in WWW-Authenticate. A rejected or mismatched payment gets this same response again with error.reason explaining why. Responses are Cache-Control no-store and expose the payment headers to browsers.
headers:
PAYMENT-REQUIRED:
description: 'Base64 JSON, x402 v2 PaymentRequired object: x402Version, resource, accepts (one entry per network with scheme, network, amount in atomic USDC, asset, payTo, maxTimeoutSeconds, extra), extensions.'
schema:
type: string
WWW-Authenticate:
description: 'Stripe MPP challenge, only when the price is at least $0.50: `Payment id="...", realm="api.deadsimple.email", method="stripe", intent="charge", expires="<ISO 8601>", request="<base64url JCS JSON {amount (cents), currency, description, methodDetails}>"`. Valid five minutes, single use.'
schema:
type: string
Cache-Control:
schema:
type: string
example: no-store
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentRequiredBody'
parameters:
WorkspaceHeader:
name: X-Workspace-Id
in: header
required: false
schema:
type: string
format: uuid
description: Target a specific workspace. Required for account-wide API keys that want workspace-scoped results.
securitySchemes:
BearerAuth:
type: http
scheme: bearer
description: API key (dse_...), session token (JWT), or OAuth 2.1 access token (dse_oat_..., obtained via the authorization-code + PKCE flow below)
OAuth2:
type: oauth2
description: OAuth 2.1 for MCP clients. Public clients only (PKCE S256 required, no client secret). Supports RFC 7591 dynamic client registration at /oauth/register and discovery via /.well-known/oauth-authorization-server. Access tokens carry inbox/message permissions only (no billing, team, or API-key administration).
flows:
authorizationCode:
authorizationUrl: https://api.deadsimple.email/oauth/authorize
tokenUrl: https://api.deadsimple.email/oauth/token
refreshUrl: https://api.deadsimple.email/oauth/token
scopes:
email: Send, receive, and manage email inboxes
OpenIDConnect:
type: openIdConnect
openIdConnectUrl: https://id.deadsimple.email/.well-known/openid-configuration
description: 'Sign in with Dead Simple. Relying parties use this to authenticate an agent by its inbox; it is not used to call the Dead Simple API itself. Scopes: openid, email, profile (open clients); owner_email, org, offline_access (registered clients only).'