Développeurs · API REST

L'API AI Emaily. Votre boîte de réception, programmable.

Une API REST versionnée sur votre boîte de réception unifiée — Gmail, Outlook/Microsoft 365 et tout fournisseur IMAP — plus la couche IA par-dessus : recherche sémantique, rédaction dans votre style, le Living Brief et l'agent autonome. Clés API à portée limitée, webhooks signés, annulation à chaque envoi.

URL de base https://api.aiemaily.com/v1

Vue d'ensemble

Une API pour l'e-mail, la recherche, l'IA et l'agent

REST + JSON, versionné dans l'URL (/v1), documenté par une spécification OpenAPI 3.1 qui fait foi pour le contrat. Tout ce que l'application AI Emaily peut faire en votre nom, votre code peut le faire aussi — dans les mêmes garde-fous.

L'e-mail comme donnée

Fils, messages, brouillons, contacts, règles et calendrier de tous les fournisseurs — normalisés dans un schéma unique, paginés par curseur et cohérents en miroir.

La couche IA

Recherche sémantique, réponses RAG sur votre boîte mail, rédaction dans votre style fondée sur Personal Context, et le Living Brief en JSON structuré.

Push, pas polling

Webhooks signés pour les nouveaux e-mails, envois, actions de l'agent et briefs — HMAC-SHA256 avec rotation et relances avec backoff.

Sécurité par conception

Clés à portée limitée, envois soumis à confirmation avec fenêtres d'annulation, plafonds d'envoi quotidiens, quotas selon le plan et un journal d'audit complet.

Vous intégrez un assistant IA plutôt que du code ? Les mêmes fonctionnalités sont exposées comme outils MCP sur https://mcp.aiemaily.com — consultez la documentation du serveur MCP.

Démarrage rapide

Premier appel en moins d'une minute

Créez une clé, listez vos fils non lus, puis cherchez par sens. C'est tout ce qu'il faut pour démarrer.

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"}'

Authentification

Clés API à portée limitée

Authentification Bearer avec des clés que vous créez, limitez et révoquez dans Paramètres → Développeur. Les clés s'affichent une seule fois et ne sont stockées que sous forme de hachage.

every request
Authorization: Bearer aiem_live_3kd92mf8a1...
  • Format de clé : aiem_live_… pour la production, aiem_test_… pour les clés de test. Les préfixes peuvent être journalisés en toute sécurité ; jamais les clés complètes.
  • Les scopes sont fixés à la création. Créez une clé à portée étroite par intégration afin que la révocation de l'une n'affecte pas les autres.
  • La révocation est immédiate — les clés sont vérifiées à chaque requête. Rotation : créez la nouvelle clé, déployez-la, révoquez l'ancienne.
  • OAuth 2.1 + PKCE avec enregistrement dynamique de clients est disponible sur le serveur MCP pour les clients IA agissant en votre nom. L'OAuth à trois parties pour les applications REST tierces n'est pas encore public — contactez-nous si vous en construisez une.

Scopes

Tous les scopes de permission de l'API AI Emaily
ScopeAccorde
mail:readLire les fils, messages et pièces jointes
mail:writeArchiver, étiqueter, reporter, marquer lu/non lu, étoiler
mail:sendEnvoyer un brouillon existant (soumis à confirmation) et annuler dans la fenêtre d'annulation
drafts:readLister et lire les brouillons
drafts:writeCréer et modifier des brouillons
search:readRecherche par mot-clé, sémantique et hybride
contacts:readLister les contacts et leurs données relationnelles
contacts:writeMettre à jour les contacts — statut VIP, notes
context:readLire les profils clients et les variables typées
context:writeCréer et mettre à jour les profils clients et les variables
brief:readLire le Living Brief
ai:invokeExécuter des opérations IA (ask-inbox, rédaction IA) — consomme des crédits du plan
agent:readLire le journal des actions de l'agent (ce qu'a fait Copilot/Autopilot)
agent:runDéclencher un cycle de l'agent sur la boîte de réception, dans vos paramètres d'autorité
calendar:readLire les événements du calendrier des comptes connectés
calendar:writeCréer et supprimer des événements du calendrier
webhooks:manageCréer, faire pivoter et supprimer des endpoints de webhook
usage:readLire les quotas et soldes de crédits

Conventions

Pagination, idempotence et erreurs

Maîtrisez ces trois schémas et chaque endpoint se comportera de manière prévisible.

Pagination par curseur

Les endpoints de liste retournent { data, has_more, next_cursor }. Passez cursor pour obtenir la page suivante. Les curseurs sont opaques — ne les analysez pas.

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 }

Idempotence

Envoyez un en-tête Idempotency-Key (toute chaîne unique, p. ex. un UUID) sur les requêtes mutantes. Les relances avec la même clé retournent le résultat original plutôt que de répéter l'action. Obligatoire sur les endpoints d'envoi ; recommandé partout ailleurs.

Enveloppe d'erreur

Chaque erreur est un code stable lisible par machine, plus un message lisible et un request_id que vous pouvez communiquer au support. Le tableau complet des codes est ci-dessous.

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"
  }
}

Référence des endpoints

Tous les endpoints

Groupés par ressource, chacun avec ses paramètres et un vrai couple requête/réponse. Cherchez par chemin, scope ou fonction.

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

Événements, signature et vérification

Enregistrez un endpoint HTTPS, choisissez les événements, vérifiez les signatures. Les livraisons sont relancées avec backoff exponentiel pendant 3 jours ; un endpoint en échec persistant est désactivé automatiquement et vous recevez un e-mail.

Catalogue d'événements

message.receivedUn nouveau message arrive dans une boîte de réception connectée (post-tri, les tags sont donc déjà définis).
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.updatedChangements d'état du fil : archivé, étiqueté, reporté, état de lecture.
payload
{ "event": "thread.updated", "data": { "thread_id": "thr_9f2ka81", "changes": { "state": "archived" } } }
send.completedUn envoi a quitté la boîte d'envoi avec succès (après la fenêtre d'annulation).
payload
{ "event": "send.completed", "data": { "send_id": "snd_1vv92", "thread_id": "thr_9f2ka81", "message_id": "msg_81aa2" } }
send.failedUn envoi a échoué après plusieurs tentatives (rejet du fournisseur, échec d'authentification).
payload
{ "event": "send.failed", "data": { "send_id": "snd_1vv92", "reason": "provider_rejected", "detail": "550 mailbox unavailable" } }
draft.createdL'agent a rédigé une réponse et la retient pour relecture (Copilot).
payload
{ "event": "draft.created", "data": { "draft_id": "drf_8s31x", "thread_id": "thr_9f2ka81", "confidence": 0.93 } }
agent.actionToute action autonome de l'agent : tri, file d'attente, envoi, escalade.
payload
{ "event": "agent.action", "data": { "kind": "queued", "thread_id": "thr_2231a", "confidence": 0.95, "undo_until": "2026-07-10T15:09:00Z" } }
brief.readyLe Living Brief quotidien a terminé sa génération.
payload
{ "event": "brief.ready", "data": { "date": "2026-07-10", "needs_reply_count": 4 } }

Vérifiez chaque livraison

Chaque livraison porte X-Aiemaily-Signature: t=<unix-ts>,v1=<hmac> — un HMAC-SHA256 de ${t}.${rawBody} avec le secret de votre endpoint. Vérifiez la signature, rejetez les horodatages périmés et comparez toujours en temps constant.

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

SDKs officiels, générés depuis la spécification

Clients TypeScript et Python avec requêtes typées, erreurs typées, relances automatiques avec backoff et utilitaires de pagination. Ou générez le vôtre depuis le document OpenAPI.

@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

Erreurs

Codes d'erreur

Codes stables, partagés avec le serveur MCP. Testez error.code, jamais le texte du message.

Codes d'erreur de l'API AI Emaily
CodeHTTPSignificationQue faire
unauthorized401Token / clé API manquant, expiré ou révoqué.Relancez le flux OAuth dans votre client ou émettez une nouvelle clé API dans Paramètres → Développeur.
forbidden_scope403Le token n'a pas le scope requis par cet outil ou endpoint.Reconnectez-vous et accordez le scope sur l'écran de consentement. Les outils pour lesquels vous n'avez pas les scopes sont masqués dans la liste.
invalid_params400Les paramètres n'ont pas passé la validation (champ manquant, mauvais type, confirm n'est pas true sur un envoi).Le message d'erreur indique le paramètre en échec. Vérifiez les types dans la référence des outils/endpoints.
not_found404L'identifiant du fil, brouillon ou contact n'existe pas ou ne vous appartient pas.Les identifiants sont limités à l'utilisateur — récupérez-les avec list_inbox ou search_email.
rate_limited429Taux de requêtes par clé, par utilisateur ou par IP dépassé.Respectez Retry-After. Appelez get_usage pour voir le quota restant avant les traitements par lots.
quota_exceeded429Un quota journalier est épuisé — le plafond de requêtes ou d'envois du plan.Les quotas se réinitialisent chaque jour (UTC). Consultez get_usage ; contactez le support pour des limites plus élevées.
insufficient_credits402Les crédits IA de la période sont épuisés (plans mesurés).Achetez un rechargement, attendez la réinitialisation ou passez à BYOK (illimité).
plan_required402La fonctionnalité nécessite un plan supérieur — ex. MCP requiert Autopilot ou Team.Passez à un plan supérieur sur aiemaily.com/pricing, ou demandez à l'administrateur de votre équipe d'ajouter un siège.
internal_error500Un problème est survenu de notre côté.Il est possible de réessayer une fois. Chaque réponse contient un request_id — communiquez-le au support.

Limites d'utilisation et plans

Quotas par plan

Plafonds quotidiens de requêtes et d'envois fixés par plan, plus un plafond de rafale par minute par clé (120/min), par utilisateur (300/min) et par IP (300/min). Chaque réponse porte X-RateLimit-Limit / -Remaining / -Reset ; les 429 incluent Retry-After.

Limites d'utilisation de l'API AI Emaily par plan
PlanAccès APIRequêtes / jourEnvois / jourEndpoints webhook
Free
Pro · 20 $/mois✓ core scopes5,0002002
Autopilot · 40 $/mois✓ all scopes + MCP20,0001,00010
Team · à partir de 25 $/siège✓ org keys + MCPMutualisé par siègeMutualisé20
  • Les endpoints IA (/ai/*, /brief/generate, rédaction avec use_ai, recherche sémantique) consomment en plus des crédits IA du plan. Les plans BYOK sont illimités.
  • Les nouvelles clés fonctionnent avec des plafonds d'envoi réduits pendant leurs 24 premières heures (protection anti-abus).
  • Besoin de plus ? Contactez-nous pour des limites supérieures.

FAQ

Questions fréquentes

Qu'est-ce que l'API AI Emaily ?

Une API REST versionnée (api.aiemaily.com/v1) qui expose votre boîte de réception unifiée — Gmail, Outlook/Microsoft 365 et tout fournisseur IMAP — ainsi que la couche IA d'AI Emaily (recherche sémantique, rédaction dans votre style, le Living Brief, l'agent autonome) à vos propres scripts, backends et outils d'automatisation.

Comment obtenir une clé API ?

Paramètres → Développeur → Clés API dans l'application AI Emaily (plan Pro ou supérieur). Les clés ont des scopes fixes à la création, s'affichent une seule fois et ne sont stockées que sous forme de hachage. Utilisez une clé à portée étroite par intégration.

Quels plans incluent l'accès à l'API ?

Pro (20 $/mois) inclut l'API REST avec les scopes de base. Autopilot (40 $/mois) ajoute les scopes d'agent et le serveur MCP. Team (à partir de 25 $/siège) ajoute les clés d'organisation et les quotas mutualisés. Le plan Free n'a pas accès à l'API.

Y a-t-il une spécification OpenAPI ?

Oui — le document OpenAPI 3.1 lisible par machine est disponible sur api.aiemaily.com/v1/openapi.json et fait foi pour l'ensemble du contrat. Les SDKs TypeScript et Python sont générés à partir de lui, et vous pouvez générer des clients pour n'importe quel autre langage.

Comment les webhooks restent-ils sécurisés ?

Chaque livraison est signée : l'en-tête X-Aiemaily-Signature porte un horodatage et un HMAC-SHA256 du payload avec le secret de votre endpoint. Vérifiez la signature et rejetez les horodatages périmés (de plus de 5 minutes) pour prévenir les attaques de rejeu. Les secrets pivotent avec une fenêtre de double validité de 24 heures.

Que se passe-t-il si je dépasse une limite de taux ?

Vous recevez un 429 avec un en-tête Retry-After. Un plafond de rafale par minute (rate_limited) s'applique par clé, par utilisateur et par IP ; les quotas quotidiens de requêtes et d'envois (quota_exceeded) sont fixés par le plan. Consultez GET /v1/usage (ou les en-têtes X-RateLimit sur toute réponse) pour cadencer les traitements par lots.

L'API peut-elle contourner les paramètres de sécurité de l'application ?

Non. L'API utilise le même noyau d'application délimité par propriété que l'application et le serveur MCP : les envois requièrent une confirmation explicite et comptent contre les plafonds quotidiens, les exécutions de l'agent respectent vos modes d'autorité et vos listes d'expéditeurs autorisés, et chaque mutation est auditée.

Mon contenu e-mail est-il utilisé pour entraîner des modèles ?

Jamais. Accords de rétention zéro avec les fournisseurs de modèles et aucun entraînement sur les e-mails des utilisateurs — la même politique de confidentialité qui régit l'application couvre chaque appel API et MCP.

Versionnement et changelog

Un contrat sur lequel vous pouvez construire

Version majeure dans l'URL (/v1). Les changements additifs — nouveaux endpoints, nouveaux champs optionnels — sont publiés en continu et ne cassent jamais les clients existants. Les changements cassants passent en /v2 avec au moins 6 mois d'avis de dépréciation en parallèle, annoncés ici et par e-mail à tous les propriétaires de clés actives.

v1.0.0

2026 — GAactuel
  • Première publication publique : 21 outils couvrant la lecture, la recherche & IA, l'organisation, la rédaction & l'envoi, et le contexte & les informations.
  • JSON-RPC HTTP streamable sur https://mcp.aiemaily.com, négociant le protocole MCP 2025-11-25 ; les outils portent des annotations (indicateurs readOnly / destructive / idempotent).
  • OAuth 2.1 avec PKCE et enregistrement dynamique de clients — l'écran de consentement affiche un badge vérifié/non vérifié et l'hôte de redirection, et restreint les scopes privilégiés ; porteur de clé API (aiem_live_…) pour les clients headless.
  • Outils de recherche et de récupération compatibles ChatGPT, pour que les connecteurs de recherche approfondie et de connaissance d'entreprise fonctionnent sans configuration supplémentaire.
  • Envoi en deux étapes soumis à confirmation avec fenêtre d'annulation côté serveur ; délimitation du contenu non fiable sur toutes les sorties dérivées des e-mails.
  • Limites de taux par clé, par utilisateur et par IP, plafonds quotidiens de requêtes/envois et journalisation d'audit complète.

v0.9.0

2026 — bêta privée
  • Bêta partenaires de conception sur l'API REST v1.
  • Ajout de get_attachment_text et list_agent_actions d'après les retours de la bêta.
  • Écran de consentement réécrit en langage produit (scope → anglais simple).

Construisez sur votre boîte de réception.

Créez une clé, faites un appel, publiez quelque chose. L'accès à l'API commence au plan Pro ; les agents obtiennent le serveur MCP avec Autopilot.