{"openapi":"3.1.0","info":{"title":"apumail","version":"0.1.0","summary":"Agent-native temp mail — receive-only inbox + outbound send via Resend.","description":"Provision an inbox at <local>@apumail.com, receive emails (verification codes, OTPs, signup confirms), optionally send back from the same address. Free tier inbox auto-expires 24h after last activity.","contact":{"name":"apumail.com","url":"https://apumail.com"},"license":{"name":"UNLICENSED"}},"servers":[{"url":"https://api.apumail.com"}],"components":{"securitySchemes":{"InboxToken":{"type":"http","scheme":"bearer","description":"Per-inbox token returned ONCE at provisioning time. Pass as `Authorization: Bearer <token>` or `X-Inbox-Token: <token>`."}},"schemas":{"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string"},"code":{"type":"string"},"hint":{"type":"string"},"retry_after_seconds":{"type":"integer"}}},"Mail":{"type":"object","required":["id","to","from","subject","text","received_at"],"properties":{"id":{"type":"integer","description":"Monotonic per-server; use as `since` cursor."},"to":{"type":"string","format":"email"},"from":{"type":"string"},"subject":{"type":"string"},"text":{"type":"string"},"html":{"type":["string","null"]},"received_at":{"type":"integer","description":"Unix ms."},"otp":{"type":["string","null"],"description":"Extracted verification code if one was detected (regex-based)."}}},"InboxCreated":{"type":"object","required":["address","token","expires_at_ms","ttl_seconds"],"properties":{"address":{"type":"string","format":"email"},"token":{"type":"string","description":"Shown ONCE. Save it."},"expires_at_ms":{"type":"integer"},"ttl_seconds":{"type":"integer"},"notice":{"type":"string"}}},"InboxList":{"type":"object","properties":{"address":{"type":"string","format":"email"},"count":{"type":"integer"},"next_since":{"type":"integer"},"expires_at_ms":{"type":["integer","null"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Mail"}}}},"WebhookCreated":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"secret":{"type":"string","description":"Shown ONCE. Save it for HMAC verification."},"created_at":{"type":"integer"},"signature_scheme":{"type":"string"}}},"WebhookRecord":{"type":"object","properties":{"id":{"type":"string"},"to_address":{"type":"string","format":"email"},"url":{"type":"string","format":"uri"},"created_at":{"type":"integer"},"last_delivered_at":{"type":["integer","null"]},"last_status":{"type":["integer","null"]},"failure_count":{"type":"integer"},"disabled":{"type":"integer","description":"0 or 1; 1 = auto-disabled after consecutive failures."}}},"WebhookList":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookRecord"}}}}}},"paths":{"/api/v1/health":{"get":{"summary":"Service health + counters.","responses":{"200":{"description":"OK"}}}},"/api/v1/info":{"get":{"summary":"JSON capabilities + companion services.","responses":{"200":{"description":"Service descriptor."}}}},"/api/v1/inboxes":{"post":{"summary":"Provision an inbox (free, or verified-human via notlogin).","description":"No auth required. Three tiers:\n• FREE (default, empty body): random <adj>-<noun>-<hex>@apumail.com slug, expires 24h after last activity. Rate-limited 10 burst / 5-per-min per IP. Passing `local_part` here is rejected (vanity needs a tier).\n• VERIFIED-HUMAN: pass `notlogin_credential` (a Verifiable Credential exported from notlogin.com for vendor 'apumail', proofs ['email']). Unlocks ONE permanent inbox — vanity `local_part` if supplied, else a random slug — with a bigger send quota. One inbox per credential. Bypasses the per-IP provision limit (gated by the signed credential). See companion service notlogin in /api/v1/info.\n• PAID: vanity <handle>@apumail.com via a rogerthat handle (mint at rogerthat.chat, then /auth/rogerthat/callback).\nToken shown ONCE.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"local_part":{"type":"string","description":"Desired vanity local part (the part before @). Requires the verified-human (notlogin) or paid (rogerthat) tier; ignored/rejected on the free tier."},"notlogin_credential":{"type":"object","description":"A notlogin Verifiable Credential (the exported VC JSON, vendor 'apumail', proofs ['email']). When valid, provisions the verified-human tier. Verified online against notlogin.com."},"agent":{"type":"string","description":"Optional agent label for attribution."}}}}}},"responses":{"201":{"description":"Inbox created. Verified-human responses include `tier: \"verified-human\"`, `permanent: true`, `ttl_seconds: null`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxCreated"}}}},"400":{"description":"Vanity local_part without a tier, or bad local_part."},"403":{"description":"notlogin credential rejected (invalid/expired/revoked/wrong vendor). Code `notlogin_invalid`."},"409":{"description":"Address taken, or this notlogin credential already provisioned an inbox (code `credential_redeemed`)."},"429":{"description":"Rate limited (free tier)."}}}},"/api/v1/inbox/{address}":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}}],"get":{"summary":"List mails in the inbox.","security":[{"InboxToken":[]}],"parameters":[{"name":"since","in":"query","schema":{"type":"integer"},"description":"Return mails with id > this."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Mail list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxList"}}}},"401":{"description":"Missing/invalid token."}}},"delete":{"summary":"Delete the inbox + all its mails. Idempotent.","security":[{"InboxToken":[]}],"responses":{"200":{"description":"Deleted."},"401":{"description":"Missing/invalid token."}}}},"/api/v1/inbox/{address}/latest":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}}],"get":{"summary":"Most recent mail + its extracted OTP, if any.","security":[{"InboxToken":[]}],"responses":{"200":{"description":"Latest mail."}}}},"/api/v1/inbox/{address}/wait":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}}],"get":{"summary":"Long-poll for new mail.","description":"Blocks up to `timeout` seconds waiting for mail with id > `since`. Returns immediately if any such mail already exists.","security":[{"InboxToken":[]}],"parameters":[{"name":"since","in":"query","schema":{"type":"integer"}},{"name":"timeout","in":"query","schema":{"type":"integer","minimum":1,"maximum":300,"default":60}}],"responses":{"200":{"description":"Mail (or timed_out: true)."}}}},"/api/v1/inbox/{address}/send":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}}],"post":{"summary":"Send an outbound mail FROM this inbox (no spoofing).","description":"From is forced to the authenticated address. Rate-limited 5/h per inbox AND 20/h per IP — aggressive, send abuse destroys domain reputation.","security":[{"InboxToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","subject"],"properties":{"to":{"oneOf":[{"type":"string","format":"email"},{"type":"array","items":{"type":"string","format":"email"},"maxItems":50}]},"subject":{"type":"string","maxLength":256},"text":{"type":"string","maxLength":100000},"html":{"type":"string","maxLength":100000},"reply_to":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Accepted by Resend."},"400":{"description":"Bad input."},"401":{"description":"Missing/invalid token."},"429":{"description":"Rate limited (per-inbox OR per-IP)."},"502":{"description":"Resend rejected."},"503":{"description":"RESEND_API_KEY not configured."}}}},"/api/v1/inbox/{address}/webhooks":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}}],"get":{"summary":"List webhooks for this inbox.","security":[{"InboxToken":[]}],"responses":{"200":{"description":"Webhook list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}}},"401":{"description":"Missing/invalid token."}}},"post":{"summary":"Register a webhook URL for push notifications on new mail.","description":"Max 5 webhooks per inbox. Server POSTs `{event:'mail.received', inbox, mail, ts}` with HMAC-SHA256 in X-Apumail-Signature. Retries 3x with backoff; disables after 10 consecutive failures.","security":[{"InboxToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"responses":{"201":{"description":"Webhook created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"Invalid URL."},"404":{"description":"Inbox not found."},"409":{"description":"Inbox already at max webhooks."}}}},"/api/v1/inbox/{address}/webhooks/{id}":{"parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","format":"email"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"delete":{"summary":"Unregister a webhook by id.","security":[{"InboxToken":[]}],"responses":{"200":{"description":"Removed (or already gone)."}}}},"/mcp":{"post":{"summary":"Streamable HTTP MCP transport (JSON-RPC 2.0).","description":"Methods: initialize, tools/list, tools/call. Tools: create_inbox, wait_for_mail, read_inbox, extract_latest_otp, delete_inbox, send_mail, register_webhook, list_webhooks, delete_webhook.","responses":{"200":{"description":"JSON-RPC response."}}},"get":{"summary":"MCP descriptor (human-readable, lists transport + tool names).","responses":{"200":{"description":"Descriptor JSON."}}}},"/openapi.json":{"get":{"summary":"This document.","responses":{"200":{"description":"OpenAPI 3.1 spec for the public REST surface."}}}},"/llms.txt":{"get":{"summary":"Agent-readable bootstrap (markdown).","description":"Drop this into any agent context to teach it how to use apumail in one shot.","responses":{"200":{"description":"Quickstart markdown.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/.well-known/mcp.json":{"get":{"summary":"MCP descriptor (machine-readable).","responses":{"200":{"description":"MCP descriptor JSON (transport + endpoint + tool names)."}}}},"/favicon.svg":{"get":{"summary":"Llama mascot favicon.","responses":{"200":{"description":"SVG image.","content":{"image/svg+xml":{"schema":{"type":"string"}}}}}}},"/robots.txt":{"get":{"summary":"Robot directives — fully indexable on purpose (public docs).","responses":{"200":{"description":"robots.txt body."}}}}}}