Entwickler · REST-API

Die AI Emaily API. Dein Posteingang, programmierbar.

Eine versionierte REST-API über deinen einheitlichen Posteingang — Gmail, Outlook/Microsoft 365 und jeder IMAP-Anbieter — plus die KI-Schicht darüber: semantische Suche, stimmgerechte Entwürfe, das Living Brief und der autonome Agent. Scope-begrenzte API-Schlüssel, signierte Webhooks, Rückgängig bei jedem Versand.

Basis-URL https://api.aiemaily.com/v1

Überblick

Eine API für E-Mail, Suche, KI und den Agenten

REST + JSON, versioniert in der URL (/v1), dokumentiert durch eine OpenAPI-3.1-Spezifikation als einzige Wahrheitsquelle des Vertrags. Alles, was die AI Emaily App in deinem Namen tun kann, kann dein Code ebenfalls tun — innerhalb derselben Sicherheitsmechanismen.

E-Mail als Daten

Threads, Nachrichten, Entwürfe, Kontakte, Regeln und Kalender über alle Anbieter hinweg — normalisiert in einem einheitlichen Schema, cursor-paginiert und spiegelkonsistent.

Die KI-Schicht

Semantische Suche, RAG-Antworten über dein Postfach, stimmgerechte Entwürfe auf Basis von Personal Context und das Living Brief als strukturiertes JSON.

Push statt Polling

Signierte Webhooks für neue E-Mails, Versendungen, Agentenaktionen und Briefs — HMAC-SHA256 mit Rotation und Wiederholungsversuchen mit Backoff.

Sicherheit durch Design

Scope-begrenzte Schlüssel, bestätigungspflichtige Versendungen mit Rückgängig-Fenstern, tägliche Versandlimits, planabhängige Kontingente und ein vollständiges Auditprotokoll.

Baust du für einen KI-Assistenten statt für Code? Dieselben Funktionen sind als MCP-Tools unter https://mcp.aiemaily.com verfügbar — siehe die MCP-Server-Dokumentation.

Schnellstart

Erster Aufruf in weniger als einer Minute

Erstelle einen Schlüssel, liste deine ungelesenen Threads auf und suche nach Bedeutung. Das ist der gesamte Einstieg.

terminal
# 1. Create an API key at app.aiemaily.com → Settings → Developer
export AIEMAILY_API_KEY="aiem_live_..."

# 2. Your first call — what's unread in the inbox?
curl "https://api.aiemaily.com/v1/threads?state=inbox&unread=true&limit=5" \
  -H "Authorization: Bearer $AIEMAILY_API_KEY"

# 3. Search by meaning, not keywords
curl -X POST https://api.aiemaily.com/v1/search \
  -H "Authorization: Bearer $AIEMAILY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "the invoice Marta chased last month", "mode": "semantic"}'

Authentifizierung

Scope-begrenzte API-Schlüssel

Bearer-Authentifizierung mit Schlüsseln, die du erstellst, einschränkst und unter Einstellungen → Entwickler widerrufst. Schlüssel werden einmalig angezeigt und nur als Hash gespeichert.

every request
Authorization: Bearer aiem_live_3kd92mf8a1...
  • Schlüsselformat: aiem_live_… für die Produktion, aiem_test_… für Testschlüssel. Präfixe sind sicher zu protokollieren; vollständige Schlüssel niemals.
  • Scopes sind bei der Erstellung festgelegt. Erstelle pro Integration einen eng begrenzten Schlüssel, damit das Widerrufen eines Schlüssels die anderen nicht beeinträchtigt.
  • Widerruf ist sofort wirksam — Schlüssel werden bei jeder Anfrage geprüft. Rotation: neuen Schlüssel erstellen, deployen, alten widerrufen.
  • OAuth 2.1 + PKCE mit dynamischer Client-Registrierung ist auf dem MCP-Server für KI-Clients verfügbar, die in deinem Namen handeln. Drei-Parteien-OAuth für Drittanbieter-REST-Apps ist noch nicht öffentlich — kontaktiere uns, wenn du eine baust.

Scopes

Alle Berechtigungs-Scopes der AI Emaily API
ScopeGewährt
mail:readThreads, Nachrichten und Anhänge lesen
mail:writeArchivieren, beschriften, zurückstellen, als gelesen/ungelesen markieren, markieren
mail:sendEinen vorhandenen Entwurf versenden (bestätigungspflichtig) und innerhalb des Rückgängig-Fensters abbrechen
drafts:readEntwürfe auflisten und lesen
drafts:writeEntwürfe erstellen und bearbeiten
search:readSchlüsselwort-, semantische und hybride Suche
contacts:readKontakte und Beziehungsdaten auflisten
contacts:writeKontakte aktualisieren — VIP-Status, Notizen
context:readClient-Profile und typisierte Variablen lesen
context:writeClient-Profile und Variablen erstellen und aktualisieren
brief:readDas Living Brief lesen
ai:invokeKI-Operationen ausführen (ask-inbox, KI-Entwurf) — verbraucht Plan-Guthaben
agent:readDas Aktionsprotokoll des Agenten lesen (was Copilot/Autopilot getan hat)
agent:runEinen Agentendurchlauf über den Posteingang starten, innerhalb deiner Berechtigungseinstellungen
calendar:readKalenderereignisse aus verbundenen Konten lesen
calendar:writeKalenderereignisse erstellen und löschen
webhooks:manageWebhook-Endpunkte erstellen, rotieren und löschen
usage:readKontingente und Guthaben lesen

Konventionen

Paginierung, Idempotenz und Fehler

Lerne diese drei Muster und jeder Endpunkt verhält sich vorhersehbar.

Cursor-Paginierung

Listen-Endpunkte geben { data, has_more, next_cursor } zurück. Übergib cursor, um die nächste Seite abzurufen. Cursor sind opak — parse sie nicht.

pagination
GET /v1/threads?limit=25
→ { "data": [...25 threads...], "has_more": true, "next_cursor": "eyJvZmZzZXQiOjI1fQ" }

GET /v1/threads?limit=25&cursor=eyJvZmZzZXQiOjI1fQ
→ { "data": [...], "has_more": false }

Idempotenz

Sende einen Idempotency-Key-Header (beliebige eindeutige Zeichenkette, z. B. eine UUID) bei mutierenden Anfragen. Wiederholungen mit demselben Schlüssel geben das ursprüngliche Ergebnis zurück, anstatt die Aktion zu wiederholen. Pflicht bei Versand-Endpunkten; überall sonst empfohlen.

Fehler-Envelope

Jeder Fehler ist ein stabiler maschinenlesbarer Code plus eine menschenlesbare Nachricht und eine request_id, die du dem Support mitteilen kannst. Die vollständige Code-Tabelle ist weiter unten.

error · 400
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_params",
    "message": "Sending requires confirm: true — sends are never implicit.",
    "param": "confirm",
    "request_id": "req_8f31ka92"
  }
}

Endpunkt-Referenz

Alle Endpunkte

Nach Ressource gruppiert, jeweils mit Parametern und einem echten Anfrage-/Antwortpaar. Suche nach Pfad, Scope oder Funktion.

Accounts

The mailboxes connected to the AI Emaily account. Read-only over the API — connecting a mailbox happens in-app (OAuth with the provider).

GET/accountsmail:readList connected mailboxes.
response
{
  "data": [
    { "id": "acc_71b2", "provider": "gmail", "email": "[email protected]", "status": "active" },
    { "id": "acc_98x1", "provider": "outlook", "email": "[email protected]", "status": "active" }
  ],
  "has_more": false
}

Threads

Conversations across every connected mailbox. State changes mirror back to Gmail/Outlook/IMAP so all clients stay consistent.

GET/threadsmail:readList threads, filtered and cursor-paginated.
Parameters for GET /threads
ParameterInTypeDescription
statequeryinbox | archived | snoozed | trash | sentThread state. Default inbox.
tabqueryprimary | social | promotions | updatesCategory tab filter.
labelquerystringOnly threads carrying this label.
account_idquerystringRestrict to one mailbox.
unreadquerybooleanOnly unread threads.
limitquerynumber (1–100)Page size. Default 25.
cursorquerystringCursor from a previous page.
request
GET /v1/threads?state=inbox&tab=primary&unread=true&limit=10
response
{
  "data": [
    {
      "id": "thr_9f2ka81",
      "account_id": "acc_71b2",
      "subject": "Contract renewal — need your sign-off by Friday",
      "from": { "name": "Dana Whitfield", "email": "[email protected]" },
      "snippet": "Hi — legal cleared the redlines. Can you confirm the...",
      "labels": ["important", "client/acme"],
      "unread": true,
      "message_count": 4,
      "updated_at": "2026-07-10T14:22:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJvZmZzZXQiOjEwfQ"
}
GET/threads/{id}mail:readGet one thread with its message list.
Parameters for GET /threads/{id}
ParameterInTypeDescription
id*pathstringThread id.
response
{
  "id": "thr_9f2ka81",
  "subject": "Contract renewal — need your sign-off by Friday",
  "participants": ["[email protected]", "[email protected]"],
  "labels": ["important", "client/acme"],
  "state": "inbox",
  "messages": [{ "id": "msg_77xk1", "from": { "email": "[email protected]" }, "date": "2026-07-10T14:22:00Z", "snippet": "Hi — legal cleared..." }]
}
PATCH/threads/{id}mail:writeMutate thread state — read, star, archive, labels, snooze.

All mutations are reversible and mirror to the provider. Combine multiple changes in one call.

Parameters for PATCH /threads/{id}
ParameterInTypeDescription
readbodybooleanMark read/unread.
starredbodybooleanStar / unstar.
statebodyinbox | archived | trash | spamMove the thread.
add_labels / remove_labelsbodystring[]Label changes. New labels are created on first use.
snooze_untilbodyISO 8601 datetime | nullSnooze; null unsnoozes.
request
{ "read": true, "add_labels": ["client/acme"], "state": "archived" }
response
{ "id": "thr_9f2ka81", "state": "archived", "labels": ["important", "client/acme"], "unread": false }
GET/threads/{id}/messagesmail:readList full messages in a thread.
Parameters for GET /threads/{id}/messages
ParameterInTypeDescription
include_quotedquerybooleanInclude quoted reply history in bodies. Default false.
response
{
  "data": [
    {
      "id": "msg_77xk1",
      "from": { "name": "Dana Whitfield", "email": "[email protected]" },
      "to": [{ "email": "[email protected]" }],
      "date": "2026-07-10T14:22:00Z",
      "body_text": "Hi — legal cleared the redlines. Can you confirm the renewal terms by Friday? — Dana",
      "attachments": [{ "id": "att_3m1", "name": "renewal-v4.pdf", "size": 182044, "mime": "application/pdf" }]
    }
  ],
  "has_more": false
}

Messages & attachments

Individual messages, decrypted bodies, and attachment downloads. Bodies are stored encrypted; decryption happens server-side per request.

GET/messages/{id}mail:readGet one message with metadata and text body.
response
{
  "id": "msg_77xk1",
  "thread_id": "thr_9f2ka81",
  "from": { "name": "Dana Whitfield", "email": "[email protected]" },
  "date": "2026-07-10T14:22:00Z",
  "body_text": "Hi — legal cleared the redlines...",
  "headers": { "message_id": "<[email protected]>" }
}
GET/messages/{id}/bodymail:readStream the full sanitized HTML body.

Returns text/html. Tracking pixels are stripped and links sandboxed, exactly as in the app.

response
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

<!doctype html><html>…sanitized message body…</html>
GET/messages/{id}/attachments/{att_id}mail:readDownload an attachment (streamed).
response
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="renewal-v4.pdf"

<binary>

Drafts & sending

The recommended flow is draft-then-send: iterate on a draft, then commit with an explicit send call. Idempotency-Key is required on sends.

GET/draftsdrafts:readList drafts.
response
{
  "data": [{ "id": "drf_8s31x", "to": ["[email protected]"], "subject": "Re: Contract renewal", "status": "draft", "updated_at": "2026-07-10T14:50:00Z" }],
  "has_more": false
}
POST/draftsdrafts:writeCreate a draft — plain, or AI-written in the user's voice.

With use_ai: true, AI Emaily writes the body from instructions using the user's Personal Context (spends 1 AI credit).

Parameters for POST /drafts
ParameterInTypeDescription
account_idbodystringSending mailbox. Default: primary account.
reply_to_thread_idbodystringCompose as a reply to this thread.
to / cc / bccbodystring[]Recipients. Inherited when replying.
subjectbodystringSubject. Inherited when replying.
bodybodystringPlain text or simple HTML body.
use_aibodybooleanHave AI Emaily write the body. Default false.
instructionsbodystringWhat the AI draft should say (with use_ai).
request
{
  "reply_to_thread_id": "thr_9f2ka81",
  "use_ai": true,
  "instructions": "Confirm the renewal at the agreed $24k/year, ask for the signature link, warm tone."
}
response
{
  "id": "drf_8s31x",
  "to": ["[email protected]"],
  "subject": "Re: Contract renewal — need your sign-off by Friday",
  "body": "Hi Dana — confirmed on our side at $24,000/year as agreed. Send over the signature link and I'll turn it around today. Best, Alex",
  "status": "draft",
  "credits_spent": 1
}
PATCH/drafts/{id}drafts:writeEdit a draft.
request
{ "body": "Hi Dana — confirmed at $24,000/year. Please send the signature link. Best, Alex" }
response
{ "id": "drf_8s31x", "status": "draft", "updated_at": "2026-07-10T14:58:00Z" }
DELETE/drafts/{id}drafts:writeDelete a draft.
response
{ "id": "drf_8s31x", "deleted": true }
POST/drafts/{id}/sendmail:sendSend a draft — explicit, capped, undoable.

Requires confirm: true and an Idempotency-Key header. Counts against the plan's daily send cap. Returns an undo handle valid for the undo window.

Parameters for POST /drafts/{id}/send
ParameterInTypeDescription
confirm*bodybooleanMust be true.
undo_window_sbodynumber (0–30)Server-side undo hold. Default 10.
scheduled_atbodyISO 8601 datetimeSchedule instead of sending now.
request
POST /v1/drafts/drf_8s31x/send
Idempotency-Key: 5f3e9c1a-renewal-reply

{ "confirm": true }
response
{
  "send_id": "snd_1vv92",
  "status": "scheduled",
  "undo_until": "2026-07-10T15:04:10Z"
}
POST/sends/{id}/cancelmail:sendCancel a send inside its undo window.

Atomic: succeeds only before dispatch. After the window, returns 409 undo_window_elapsed and the message stays sent.

response
{ "send_id": "snd_1vv92", "status": "undone", "draft_id": "drf_8s31x" }
POST/messages/sendmail:sendCompose and send in one call (scripts only).

For trusted automations. Same caps, idempotency requirement, and undo semantics as draft sends. Not exposed as an MCP tool — agents must use the draft-then-send flow.

request
POST /v1/messages/send
Idempotency-Key: 91c2ab-receipt-fwd

{
  "account_id": "acc_71b2",
  "to": ["[email protected]"],
  "subject": "June receipts",
  "body": "Attached digest of June receipts.",
  "undo_window_s": 10
}
response
{ "send_id": "snd_8a2m1", "status": "scheduled", "undo_until": "2026-07-10T15:10:10Z" }

Contacts

People, enriched with relationship signals from your mail history.

GET/contactscontacts:readList or search contacts.
Parameters for GET /contacts
ParameterInTypeDescription
qquerystringName, email, or domain filter.
vipquerybooleanOnly VIPs.
response
{
  "data": [{ "id": "cnt_11a", "name": "Dana Whitfield", "email": "[email protected]", "vip": true, "last_contact": "2026-07-10", "threads": 42 }],
  "has_more": false
}
PATCH/contacts/{id}contacts:writeUpdate a contact (VIP flag, notes).
request
{ "vip": true, "notes": "VP Ops at Acme — renewal owner." }
response
{ "id": "cnt_11a", "vip": true }

Rules

Inbox rules (filing, labeling) and agent rules (per-sender authority). Conditions and actions are validated against the same allowlist the app uses.

GET/rulesmail:readList rules.
response
{
  "data": [
    {
      "id": "rul_5m2",
      "kind": "inbox",
      "conditions": [{ "field": "from_domain", "op": "equals", "value": "acme.com" }],
      "actions": [{ "type": "addLabel", "value": "client/acme" }],
      "priority": 10
    }
  ]
}
POST/rulesmail:writeCreate a rule.
request
{
  "kind": "inbox",
  "conditions": [{ "field": "subject", "op": "contains", "value": "invoice" }],
  "actions": [{ "type": "addLabel", "value": "finance" }, { "type": "setCategory", "value": "updates" }]
}
response
{ "id": "rul_9k1", "kind": "inbox", "priority": 20 }
DELETE/rules/{id}mail:writeDelete a rule.
response
{ "id": "rul_9k1", "deleted": true }

Personal Context

The Context & Variables Engine: per-client profiles keyed to email domains, plus typed variables with per-client overrides. What makes AI drafts factually right.

GET/context/profilescontext:readList client/domain profiles.
response
{
  "data": [{ "id": "ctx_acme", "domain": "acme.com", "name": "Acme Corp", "updated_at": "2026-07-10T15:00:00Z" }],
  "has_more": false
}
GET/context/profiles/{id}context:readGet one profile with its variables.
response
{
  "id": "ctx_acme",
  "domain": "acme.com",
  "summary": "Enterprise client since 2024. Renewal cycle: August. Main contact Dana Whitfield (VP Ops).",
  "variables": { "client.name": "Acme Corp", "pricing.renewal": "$24,000/year", "tone": "warm, concise" }
}
PATCH/context/profiles/{id}context:writeUpdate profile variables or append a note.
request
{ "set": { "pricing.renewal": "$24,000/year" }, "append_note": "Renewal confirmed 2026-07-10 at $24k/yr." }
response
{ "id": "ctx_acme", "updated_keys": ["pricing.renewal"] }
GET/context/variablescontext:readList global typed variables.
response
{ "data": [{ "key": "pricing.pro", "value": "$20/month", "type": "string" }] }
GET/context/filescontext:readList the reference files attached to Personal Context.

Documents you've added to the context brain (rate cards, playbooks, boilerplate) that ground AI drafting. Returns metadata; bodies are fetched by id.

response
{
  "data": [{ "id": "ctf_5a2", "name": "2026-rate-card.pdf", "bytes": 84213, "profile_id": "ctx_acme", "created_at": "2026-07-01T10:00:00Z" }],
  "has_more": false
}

Brief

The Living Brief as structured data — read the latest, or generate a fresh one on demand. Generation is credit-metered (BYOK plans uncapped) and reports credits_spent.

GET/brief/latestbrief:readGet the latest Living Brief.
response
{
  "date": "2026-07-10",
  "needs_reply": [{ "thread_id": "thr_9f2ka81", "from": "[email protected]", "why": "Sign-off requested by Friday" }],
  "commitments": [{ "thread_id": "thr_5h2m6", "promise": "Send Q3 proposal to Linh", "due": "2026-07-11" }],
  "calendar": [{ "title": "Acme renewal call", "start": "2026-07-10T16:00:00Z" }]
}
POST/brief/generateai:invokeGenerate a fresh brief now (spends AI credits).
response
{ "date": "2026-07-10", "status": "ready", "credits_spent": 3 }

Calendar

Google Calendar events synced through connected accounts.

GET/calendar/eventscalendar:readList events in a window.
Parameters for GET /calendar/events
ParameterInTypeDescription
from / to*queryISO 8601 datetimeWindow bounds.
response
{ "data": [{ "id": "evt_3k1", "title": "Acme renewal call", "start": "2026-07-10T16:00:00Z", "end": "2026-07-10T16:30:00Z" }] }
POST/calendar/eventscalendar:writeCreate an event.
request
{ "title": "Contract review", "start": "2026-07-14T15:00:00Z", "end": "2026-07-14T15:30:00Z", "attendees": ["[email protected]"] }
response
{ "id": "evt_9m4", "status": "confirmed" }
DELETE/calendar/events/{id}calendar:writeDelete an event.
response
{ "id": "evt_9m4", "deleted": true }

Webhooks

Push instead of poll: register HTTPS endpoints and receive signed events. Retries with exponential backoff for 3 days, then the endpoint auto-disables (you get an email).

GET/webhookswebhooks:manageList webhook endpoints.
response
{ "data": [{ "id": "whk_2d1", "url": "https://yourco.com/hooks/aiemaily", "events": ["message.received"], "status": "active" }] }
POST/webhookswebhooks:manageRegister an endpoint. The signing secret is returned once.
request
{ "url": "https://yourco.com/hooks/aiemaily", "events": ["message.received", "send.completed"] }
response
{
  "id": "whk_2d1",
  "url": "https://yourco.com/hooks/aiemaily",
  "events": ["message.received", "send.completed"],
  "secret": "whsec_9a81c2...  // shown once — store it now",
  "status": "active"
}
POST/webhooks/{id}/rotate-secretwebhooks:manageRotate the signing secret (old secret stays valid 24h).
response
{ "id": "whk_2d1", "secret": "whsec_new...", "previous_valid_until": "2026-07-11T15:00:00Z" }
DELETE/webhooks/{id}webhooks:manageDelete an endpoint.
response
{ "id": "whk_2d1", "deleted": true }

Usage

Quota and credit visibility, so integrations can pace themselves instead of hitting 429s.

GET/usageusage:readCurrent period consumption and limits.
response
{
  "plan": "autopilot",
  "requests": { "used": 1240, "limit": 20000, "resets": "2026-07-11T00:00:00Z" },
  "sends": { "used": 12, "limit": 1000 },
  "ai_credits": { "used": 46, "limit": 1000, "byok": false }
}

Webhooks

Ereignisse, Signierung und Verifizierung

Registriere einen HTTPS-Endpunkt, wähle Ereignisse und verifiziere Signaturen. Zustellungen werden 3 Tage lang mit exponentiellem Backoff wiederholt; ein dauerhaft fehlschlagender Endpunkt wird automatisch deaktiviert und du wirst per E-Mail benachrichtigt.

Ereigniskatalog

message.receivedEine neue Nachricht trifft in einem verbundenen Posteingang ein (nach der Triage, sodass Tags bereits gesetzt sind).
payload
{
  "event": "message.received",
  "created_at": "2026-07-10T14:22:03Z",
  "data": {
    "thread_id": "thr_9f2ka81",
    "message_id": "msg_77xk1",
    "account_id": "acc_71b2",
    "from": { "email": "[email protected]" },
    "subject": "Contract renewal — need your sign-off by Friday",
    "tags": ["important", "client"]
  }
}
thread.updatedThread-Statusänderungen: archiviert, beschriftet, zurückgestellt, Lesestatus.
payload
{ "event": "thread.updated", "data": { "thread_id": "thr_9f2ka81", "changes": { "state": "archived" } } }
send.completedEin Versand hat den Ausgang erfolgreich verlassen (nach dem Rückgängig-Fenster).
payload
{ "event": "send.completed", "data": { "send_id": "snd_1vv92", "thread_id": "thr_9f2ka81", "message_id": "msg_81aa2" } }
send.failedEin Versand ist nach Wiederholungsversuchen fehlgeschlagen (Anbieter-Ablehnung, Authentifizierungsfehler).
payload
{ "event": "send.failed", "data": { "send_id": "snd_1vv92", "reason": "provider_rejected", "detail": "550 mailbox unavailable" } }
draft.createdDer Agent hat eine Antwort entworfen und hält sie zur Überprüfung bereit (Copilot).
payload
{ "event": "draft.created", "data": { "draft_id": "drf_8s31x", "thread_id": "thr_9f2ka81", "confidence": 0.93 } }
agent.actionJede autonome Aktion des Agenten: Triage, Warteschlange, Versand, Eskalation.
payload
{ "event": "agent.action", "data": { "kind": "queued", "thread_id": "thr_2231a", "confidence": 0.95, "undo_until": "2026-07-10T15:09:00Z" } }
brief.readyDas tägliche Living Brief wurde fertig generiert.
payload
{ "event": "brief.ready", "data": { "date": "2026-07-10", "needs_reply_count": 4 } }

Jede Zustellung verifizieren

Jede Zustellung trägt X-Aiemaily-Signature: t=<unix-ts>,v1=<hmac> — ein HMAC-SHA256 von ${t}.${rawBody} mit dem Geheimnis deines Endpunkts. Verifiziere die Signatur, lehne veraltete Zeitstempel ab und vergleiche immer in konstanter Zeit.

verify.ts · Node
import crypto from "node:crypto";

// X-Aiemaily-Signature: t=1760107330,v1=5257a869e7...
export function verifyAiemailySignature(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const ts = Number(parts.t);
  // Reject stale deliveries (replay protection)
  if (!ts || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  try {
    return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(parts.v1, "hex"));
  } catch {
    return false;
  }
}

SDKs

Offizielle SDKs, generiert aus der Spezifikation

TypeScript- und Python-Clients mit typisierten Anfragen, typisierten Fehlern, automatischen Wiederholungen mit Backoff und Paginierungshelfern. Oder generiere deinen eigenen aus dem OpenAPI-Dokument.

@aiemaily/sdk · npm
npm install @aiemaily/sdk

import { Aiemaily } from "@aiemaily/sdk";

const aiemaily = new Aiemaily({ apiKey: process.env.AIEMAILY_API_KEY });

// List what needs attention
const { data: threads } = await aiemaily.threads.list({ state: "inbox", unread: true });

// Semantic search
const { data: hits } = await aiemaily.search({ query: "invoices from June", mode: "semantic" });

// Draft in the user's voice, then send explicitly
const draft = await aiemaily.drafts.create({
  replyToThreadId: hits[0].threadId,
  useAi: true,
  instructions: "Confirm receipt, promise payment by Friday",
});
const send = await aiemaily.drafts.send(draft.id, { confirm: true });

// Errors are typed: err.code === "rate_limited", err.retryAfter, err.requestId

Fehler

Fehlercodes

Stabile Codes, mit dem MCP-Server geteilt. Prüfe error.code, niemals den Nachrichtentext.

Fehlercodes der AI Emaily API
CodeHTTPBedeutungLösung
unauthorized401Token / API-Schlüssel fehlt, ist abgelaufen oder widerrufen.Führe den OAuth-Fluss in deinem Client erneut aus oder erstelle einen neuen API-Schlüssel unter Einstellungen → Entwickler.
forbidden_scope403Dem Token fehlt der Scope, den dieses Tool oder dieser Endpunkt erfordert.Verbinde dich erneut und erteile den Scope auf dem Einwilligungsbildschirm. Tools, für die dir Scopes fehlen, werden aus der Tool-Liste ausgeblendet.
invalid_params400Parameter haben die Validierung nicht bestanden (fehlendes Feld, falscher Typ, confirm ist bei einem Versand nicht true).Die Fehlermeldung nennt den fehlgeschlagenen Parameter. Prüfe Typen anhand der Tool-/Endpunkt-Referenz.
not_found404Thread-, Entwurfs- oder Kontakt-ID existiert nicht oder gehört dir nicht.IDs sind benutzerbegrenzt — rufe sie erneut mit list_inbox oder search_email ab.
rate_limited429Anfragerate pro Schlüssel, pro Benutzer oder pro IP überschritten.Beachte Retry-After. Rufe get_usage auf, um das verbleibende Kontingent vor Batch-Operationen zu prüfen.
quota_exceeded429Ein Tageskontingent ist ausgeschöpft — das Anfrage- oder Versandlimit des Plans.Kontingente werden täglich zurückgesetzt (UTC). Prüfe get_usage; wende dich für höhere Limits an den Support.
insufficient_credits402KI-Guthaben für den Zeitraum sind aufgebraucht (dosierte Pläne).Kaufe ein Guthaben-Aufstockung, warte auf den Reset oder wechsle zu BYOK (unbegrenzt).
plan_required402Die Funktion erfordert einen höheren Plan — z. B. erfordert MCP Autopilot oder Team.Upgrade unter aiemaily.com/pricing oder bitte den Administrator deines Teams, einen Platz hinzuzufügen.
internal_error500Auf unserer Seite ist ein Fehler aufgetreten.Es ist sicher, einmal erneut zu versuchen. Jede Antwort enthält eine request_id — gib sie beim Kontakt mit dem Support an.

Ratenlimits und Pläne

Kontingente nach Plan

Tägliche Anfrage- und Versandlimits nach Plan, plus eine Burst-Obergrenze pro Minute pro Schlüssel (120/min), pro Benutzer (300/min) und pro IP (300/min). Jede Antwort enthält X-RateLimit-Limit / -Remaining / -Reset; 429-Antworten enthalten Retry-After.

API-Ratenlimits der AI Emaily nach Plan
PlanAPI-ZugangAnfragen / TagVersendungen / TagWebhook-Endpunkte
Free
Pro · $20/Monat✓ core scopes5,0002002
Autopilot · $40/Monat✓ all scopes + MCP20,0001,00010
Team · ab $25/Platz✓ org keys + MCPGemeinsam pro PlatzGemeinsam20
  • KI-Endpunkte (/ai/*, /brief/generate, Entwürfe mit use_ai, semantische Suche) verbrauchen zusätzlich Plan-KI-Guthaben. BYOK-Pläne sind unbegrenzt.
  • Neue Schlüssel operieren in den ersten 24 Stunden mit reduzierten Versandlimits (Missbrauchsschutz).
  • Mehr benötigt? Kontaktiere uns für höhere Limits.

FAQ

Häufig gestellte Fragen

Was ist die AI Emaily API?

Eine versionierte REST-API (api.aiemaily.com/v1), die deinen einheitlichen Posteingang — Gmail, Outlook/Microsoft 365 und jeden IMAP-Anbieter — sowie die KI-Schicht von AI Emaily (semantische Suche, stimmgerechte Entwürfe, das Living Brief, den autonomen Agenten) für eigene Skripte, Backends und Automatisierungstools zugänglich macht.

Wie erhalte ich einen API-Schlüssel?

Einstellungen → Entwickler → API-Schlüssel in der AI Emaily App (Pro-Plan oder höher). Schlüssel haben bei der Erstellung festgelegte Scopes, werden einmalig angezeigt und nur als Hash gespeichert. Verwende pro Integration einen eng begrenzten Schlüssel.

Welche Pläne beinhalten API-Zugang?

Pro ($20/Monat) enthält die REST-API mit Kern-Scopes. Autopilot ($40/Monat) ergänzt Agenten-Scopes und den MCP-Server. Team (ab $25/Platz) ergänzt Organisations-Schlüssel und gemeinsame Kontingente. Der Free-Plan hat keinen API-Zugang.

Gibt es eine OpenAPI-Spezifikation?

Ja — das maschinenlesbare OpenAPI-3.1-Dokument ist unter api.aiemaily.com/v1/openapi.json verfügbar und ist die Wahrheitsquelle für den gesamten Vertrag. Die TypeScript- und Python-SDKs werden daraus generiert, und du kannst Clients für jede andere Sprache generieren.

Wie bleiben Webhooks sicher?

Jede Zustellung ist signiert: der X-Aiemaily-Signature-Header enthält einen Zeitstempel und ein HMAC-SHA256 der Payload mit dem Geheimnis deines Endpunkts. Verifiziere die Signatur und lehne veraltete Zeitstempel (älter als 5 Minuten) ab, um Replay-Angriffe zu verhindern. Geheimnisse rotieren mit einem 24-stündigen Doppelgültigkeitsfenster.

Was passiert, wenn ich ein Ratenlimit überschreite?

Du erhältst eine 429-Antwort mit einem Retry-After-Header. Eine Burst-Obergrenze pro Minute (rate_limited) gilt pro Schlüssel, pro Benutzer und pro IP; tägliche Anfrage- und Versandlimits (quota_exceeded) werden durch den Plan festgelegt. Prüfe GET /v1/usage (oder die X-RateLimit-Header in jeder Antwort), um Batch-Jobs zu steuern.

Kann die API die Sicherheitseinstellungen der App umgehen?

Nein. Die API verwendet denselben eigentumsbegrenzten Durchsetzungskern wie die App und der MCP-Server: Versendungen erfordern explizite Bestätigung und werden gegen Tageslimits gezählt, Agentenläufe respektieren deine Berechtigungsmodi und Absender-Whitelists, und jede Mutation wird auditiert.

Wird mein E-Mail-Inhalt zum Training von Modellen verwendet?

Niemals. Null-Aufbewahrungs-Vereinbarungen mit Modellanbietern und kein Training mit E-Mails der Nutzer — dieselbe Datenschutzrichtlinie, die die App regelt, gilt für jeden API- und MCP-Aufruf.

Versionierung und Changelog

Ein Vertrag, auf dem du aufbauen kannst

Hauptversion in der URL (/v1). Additive Änderungen — neue Endpunkte, neue optionale Felder — werden kontinuierlich veröffentlicht und brechen keine bestehenden Clients. Breaking Changes kommen zu /v2 mit mindestens 6 Monaten paralleler Deprecation-Ankündigung, hier und per E-Mail an alle aktiven Schlüsselinhaber.

v1.0.0

2026 — GAaktuell
  • Erstveröffentlichung: 21 Tools in den Bereichen Lesen, Suche & KI, Organisieren, Entwerfen & Versenden sowie Kontext & Einblick.
  • Streamable-HTTP-JSON-RPC unter https://mcp.aiemaily.com, MCP-Protokoll 2025-11-25 ausgehandelt; Tools tragen Annotationen (readOnly / destructive / idempotent-Hinweise).
  • OAuth 2.1 mit PKCE und dynamischer Client-Registrierung — der Einwilligungsbildschirm zeigt ein verifiziert/nicht verifiziert-Badge und den Redirect-Host und sperrt privilegierte Scopes; API-Schlüssel-Bearer (aiem_live_…) für headless Clients.
  • ChatGPT-kompatible Such- und Abruf-Tools, sodass Deep-Research- und Unternehmens-Knowledge-Connectoren ohne zusätzliche Konfiguration funktionieren.
  • Zweistufiger bestätigungspflichtiger Versand mit serverseitigem Rückgängig-Fenster; Absicherung von nicht vertrauenswürdigem Inhalt bei allen E-Mail-abgeleiteten Ausgaben.
  • Ratenlimits pro Schlüssel, pro Benutzer und pro IP, tägliche Anfrage-/Versandlimits und vollständiges Auditprotokoll.

v0.9.0

2026 — private Beta
  • Design-Partner-Beta über die v1-REST-API.
  • get_attachment_text und list_agent_actions basierend auf Beta-Feedback hinzugefügt.
  • Einwilligungsbildschirm in Produktsprache umgeschrieben (Scope → einfaches Englisch).

Baue auf deinem Posteingang.

Erstelle einen Schlüssel, mache einen Aufruf, veröffentliche etwas. API-Zugang beginnt beim Pro-Plan; Agenten erhalten den MCP-Server beim Autopilot-Plan.