API docs

A tiny HTTP API to create test inboxes, read mail, and download attachments, great for automated tests and CI. JSON in, JSON out.

Base URL https://api.educlopedia.app

Authentication

Three ways to identify a caller, pick one:

  • API key (Pro): header Authorization: Bearer edu_live_…. Create one in your account. Inboxes you create are private (only your key/account can read them).
  • Anonymous: no header on the first call. POST /api/mailboxes returns a secret; send it back as x-mailbox-secret: <secret> on later calls. Inboxes are public (anyone with the address can read them).
  • Session: used by the web app and extension after Google sign-in. Not needed for scripting.
Public vs private. Anonymous inboxes are public (Mailinator-style), so don't put anything sensitive in them. API-key (Pro) inboxes are private to your account.

Quickstart (CI)

KEY="edu_live_…"          # from /account
BASE="https://api.educlopedia.app"

# 1) create a private inbox
MB=$(curl -s -X POST $BASE/api/mailboxes \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"localPart":"ci.run.123"}')
ID=$(echo "$MB" | jq -r .mailbox.id)
ADDR=$(echo "$MB" | jq -r .mailbox.address)   # ci.run.123@educlopedia.app

# 2) ... trigger the email to $ADDR, then poll for it ...
curl -s "$BASE/api/mailboxes/$ID/messages" -H "Authorization: Bearer $KEY"

# 3) read the full body of a message (id from step 2)
curl -s "$BASE/api/messages/<message-id>" -H "Authorization: Bearer $KEY"

Inboxes

POST /api/mailboxes

Create an inbox, or open one for a custom name you already own.

Body (all optional)

{ "localPart": "ci.run.123", "label": "my run" }
  • Omit localPart for a random address.
  • Pro keys create a private inbox (30-day retention); anonymous keys are public (24h).

Response 200/201

{ "mailbox": { "id": "mb_…", "address": "ci.run.123@educlopedia.app",
    "localPart": "ci.run.123", "domain": "educlopedia.app",
    "visibility": "private", "label": "my run",
    "createdAt": "…", "expiresAt": "…" },
  "secret": "…" }   // secret only for anonymous callers

GET /api/mailboxes

List your active inboxes (owned by your key/secret). Returns an array of mailbox objects.

GET /api/mailboxes/:id/messages

List message summaries, newest first. Add ?since=<ISO timestamp> to fetch only newer ones (poll-friendly). Add ?include=body to inline full textBody/htmlBody on each item, one call instead of two (attachments still need GET /api/messages/:id).

Response

{ "messages": [ { "id": "msg_…", "from": "Sender <a@b.com>",
      "subject": "Verify your email", "preview": "Your code is…",
      "receivedAt": "…", "isRead": false, "hasAttachments": false, "size": 1234 } ],
  "now": "2026-…Z" }

Summaries by default; ?include=body adds the bodies inline.

GET /api/mailboxes/:id/unread

Unread count: { "mailboxId": "mb_…", "unread": 2 }

POST /api/mailboxes/:id/extend

Push the expiry out by one more retention window. Returns the updated mailbox.

DELETE /api/mailboxes/:id

Delete an inbox and all its mail. 204.

Messages

GET /api/messages/:id

The full message. Marks it read.

{ "id": "msg_…", "from": "…", "subject": "…", "receivedAt": "…",
  "textBody": "plain text…",
  "htmlBody": "<sanitized html> or null",
  "attachments": [ { "id": "att_…", "filename": "x.pdf", "contentType": "application/pdf", "size": 9000 } ] }

DELETE /api/messages/:id

Delete a single message. 204.

Attachments

GET /api/attachments/:id

Download the raw attachment bytes (streamed, with content-type + content-disposition).

Account & keys

GET /api/me

{ "authenticated": true, "plan": "pro", "email": "you@…" }

Create / list / revoke API keys in your account (Pro). Keys are shown once, so store them like passwords.

Limits

  • Free / anonymous: 10 active inboxes, ~24h retention, public.
  • Pro: private inboxes, 30-day retention, API keys, generous CI rate limit.
  • Create is rate-limited (per key for Pro, per IP for anonymous). Inbound mail is capped per inbox.

Errors

Non-2xx responses are JSON: { "error": "message", "code": "rate_limited | quota_exceeded | invalid_request | not_found | unauthorized | forbidden | internal" }. 401 = bad/expired credentials; 404 = not found or not yours (private); 429 = rate-limited.

educlopedia