Desarrolladores · REST API

La API de AI Emaily. Tu bandeja de entrada, programable.

Una API REST versionada sobre tu bandeja de entrada unificada — Gmail, Outlook/Microsoft 365 y cualquier proveedor IMAP — más la capa de IA encima: búsqueda semántica, redacción con tu voz, el Living Brief y el agente autónomo. Claves de API con ámbito, webhooks firmados y función de deshacer en cada envío.

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

Resumen

Una API para correo, búsqueda, IA y el agente

REST + JSON, versionado en la URL (/v1), documentado por una especificación OpenAPI 3.1 que es la fuente de verdad del contrato. Todo lo que la aplicación AI Emaily puede hacer en tu nombre, tu código también puede hacerlo — dentro de los mismos mecanismos de seguridad.

Correo como datos

Hilos, mensajes, borradores, contactos, reglas y calendario de todos los proveedores — normalizados en un esquema unificado, paginados por cursor y con consistencia de espejo.

La capa de IA

Búsqueda semántica, respuestas RAG sobre tu bandeja, redacción con tu voz fundamentada en Personal Context y el Living Brief como JSON estructurado.

Push, no polling

Webhooks firmados para nuevo correo, envíos, acciones del agente y briefs — HMAC-SHA256 con rotación y reintentos con backoff.

Seguridad por construcción

Claves con ámbito, envíos con confirmación y ventanas de deshacer, cuotas diarias de envío, límites según el plan y un registro de auditoría completo.

¿Integras con un asistente de IA en lugar de código? Las mismas capacidades están disponibles como herramientas MCP en https://mcp.aiemaily.com — consulta la documentación del servidor MCP.

Inicio rápido

Tu primera llamada en menos de un minuto

Crea una clave, lista tus hilos no leídos y busca por significado. Eso es todo lo que necesitas para empezar.

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

Autenticación

Claves de API con ámbito

Autenticación Bearer con claves que creas, limitas y revocas en Ajustes → Desarrollador. Las claves se muestran una sola vez y se almacenan únicamente como hash.

every request
Authorization: Bearer aiem_live_3kd92mf8a1...
  • Formato de clave: aiem_live_… para producción, aiem_test_… para claves de prueba. Los prefijos son seguros para registrar; las claves completas nunca lo son.
  • Los ámbitos se fijan al crear la clave. Crea una clave con ámbito reducido por integración para que revocar una no afecte a las demás.
  • La revocación es inmediata — las claves se verifican en cada petición. Rotación: crea la nueva clave, despliégala y revoca la antigua.
  • OAuth 2.1 + PKCE con registro dinámico de clientes está disponible en el servidor MCP para clientes de IA que actúan en tu nombre. OAuth de tres patas para aplicaciones REST de terceros aún no es público — contáctanos si estás construyendo una.

Ámbitos

Todos los ámbitos de permisos de la API de AI Emaily
ÁmbitoConcede
mail:readLeer hilos, mensajes y archivos adjuntos
mail:writeArchivar, etiquetar, posponer, marcar como leído/no leído y destacar
mail:sendEnviar un borrador existente (con confirmación) y cancelar dentro de la ventana de deshacer
drafts:readListar y leer borradores
drafts:writeCrear y editar borradores
search:readBúsqueda por palabra clave, semántica e híbrida
contacts:readListar contactos y datos de relación
contacts:writeActualizar contactos — estado VIP, notas
context:readLeer perfiles de cliente y variables tipadas
context:writeCrear y actualizar perfiles de cliente y variables
brief:readLeer el Living Brief
ai:invokeEjecutar operaciones de IA (ask-inbox, redacción con IA) — consume créditos del plan
agent:readLeer el registro de acciones del agente (qué hizo Copilot/Autopilot)
agent:runIniciar un ciclo del agente sobre la bandeja de entrada, dentro de tu configuración de autoridad
calendar:readLeer eventos de calendario de las cuentas conectadas
calendar:writeCrear y eliminar eventos de calendario
webhooks:manageCrear, rotar y eliminar endpoints de webhook
usage:readLeer cuotas y saldo de créditos

Convenciones

Paginación, idempotencia y errores

Aprende estos tres patrones y cada endpoint se comportará de forma predecible.

Paginación por cursor

Los endpoints de listado devuelven { data, has_more, next_cursor }. Pasa cursor para obtener la siguiente página. Los cursores son opacos — no los analices.

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 }

Idempotencia

Envía un encabezado Idempotency-Key (cualquier cadena única, p. ej., un UUID) en peticiones mutantes. Los reintentos con la misma clave devuelven el resultado original en lugar de repetir la acción. Obligatorio en endpoints de envío; recomendado en todos los demás.

Envoltorio de error

Cada error es un código de máquina estable más un mensaje legible y un request_id que puedes dar al soporte. La tabla completa de códigos está más abajo.

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

Referencia de endpoints

Todos los endpoints

Agrupados por recurso, cada uno con parámetros y un par real de petición/respuesta. Busca por ruta, ámbito o función.

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

Eventos, firma y verificación

Registra un endpoint HTTPS, elige eventos y verifica firmas. Las entregas se reintentarán con retroceso exponencial durante 3 días; un endpoint que falle persistentemente se desactiva automáticamente y recibirás un correo.

Catálogo de eventos

message.receivedUn nuevo mensaje llega a cualquier bandeja de entrada conectada (post-triaje, por lo que las etiquetas ya están disponibles).
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.updatedCambios de estado del hilo: archivado, etiquetado, pospuesto, estado de lectura.
payload
{ "event": "thread.updated", "data": { "thread_id": "thr_9f2ka81", "changes": { "state": "archived" } } }
send.completedUn envío salió de la bandeja de salida correctamente (después de la ventana de deshacer).
payload
{ "event": "send.completed", "data": { "send_id": "snd_1vv92", "thread_id": "thr_9f2ka81", "message_id": "msg_81aa2" } }
send.failedUn envío falló tras varios reintentos (rechazo del proveedor, fallo de autenticación).
payload
{ "event": "send.failed", "data": { "send_id": "snd_1vv92", "reason": "provider_rejected", "detail": "550 mailbox unavailable" } }
draft.createdEl agente redactó una respuesta y la retiene para revisión (Copilot).
payload
{ "event": "draft.created", "data": { "draft_id": "drf_8s31x", "thread_id": "thr_9f2ka81", "confidence": 0.93 } }
agent.actionCualquier acción autónoma del agente: triaje, cola, envío, escalado.
payload
{ "event": "agent.action", "data": { "kind": "queued", "thread_id": "thr_2231a", "confidence": 0.95, "undo_until": "2026-07-10T15:09:00Z" } }
brief.readyEl Living Brief diario terminó de generarse.
payload
{ "event": "brief.ready", "data": { "date": "2026-07-10", "needs_reply_count": 4 } }

Verifica cada entrega

Cada entrega lleva X-Aiemaily-Signature: t=<unix-ts>,v1=<hmac> — un HMAC-SHA256 de ${t}.${rawBody} con el secreto de tu endpoint. Verifica la firma, rechaza marcas de tiempo antiguas y compara siempre en tiempo constante.

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 oficiales, generados desde la especificación

Clientes para TypeScript y Python con peticiones tipadas, errores tipados, reintentos automáticos con backoff y utilidades de paginación. O genera el tuyo desde el documento 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

Errores

Códigos de error

Códigos estables, compartidos con el servidor MCP. Evalúa error.code, nunca el texto del mensaje.

Códigos de error de la API de AI Emaily
CódigoHTTPSignificadoQué hacer
unauthorized401Token / clave de API faltante, caducada o revocada.Vuelve a ejecutar el flujo OAuth en tu cliente, o crea una nueva clave de API en Ajustes → Desarrollador.
forbidden_scope403El token no tiene el ámbito que requiere esta herramienta o endpoint.Reconéctate y concede el ámbito en la pantalla de consentimiento. Las herramientas para las que no tienes ámbitos están ocultas en la lista.
invalid_params400Los parámetros no superaron la validación (campo faltante, tipo incorrecto, confirm no es true en un envío).El mensaje de error indica el parámetro que falló. Verifica los tipos en la referencia de herramientas/endpoints.
not_found404El ID del hilo, borrador o contacto no existe o no te pertenece.Los IDs son de ámbito de usuario — vuelve a obtenerlos con list_inbox o search_email.
rate_limited429Se superó la tasa de peticiones por clave, por usuario o por IP.Respeta Retry-After. Llama a get_usage para ver la cuota restante antes de ejecutar procesos por lotes.
quota_exceeded429Se agotó una cuota diaria — el límite de peticiones o envíos del plan.Las cuotas se reinician diariamente (UTC). Consulta get_usage; contacta con soporte para límites más altos.
insufficient_credits402Se han agotado los créditos de IA del período (planes medidos).Compra una recarga, espera el reinicio o cambia a BYOK (sin límite).
plan_required402La funcionalidad requiere un plan superior — p. ej., MCP requiere Autopilot o Team.Actualiza en aiemaily.com/pricing o pide al administrador de tu equipo que añada un puesto.
internal_error500Algo falló en nuestro lado.Es seguro reintentar una vez. Cada respuesta incluye un request_id — inclúyelo al contactar con soporte.

Límites de uso y planes

Cuotas por plan

Límites diarios de peticiones y envíos según el plan, más un techo de ráfaga por minuto por clave (120/min), por usuario (300/min) y por IP (300/min). Cada respuesta incluye X-RateLimit-Limit / -Remaining / -Reset; los 429 incluyen Retry-After.

Límites de uso de la API de AI Emaily por plan
PlanAcceso a la APIPeticiones / díaEnvíos / díaEndpoints de webhook
Free
Pro · $20/mes✓ core scopes5,0002002
Autopilot · $40/mes✓ all scopes + MCP20,0001,00010
Team · desde $25/puesto✓ org keys + MCPAgrupado por puestoAgrupado20
  • Los endpoints de IA (/ai/*, /brief/generate, redacción con use_ai, búsqueda semántica) consumen créditos de IA del plan. Los planes BYOK no tienen límite.
  • Las claves nuevas operan con cuotas de envío reducidas durante sus primeras 24 horas (protección anti-abuso).
  • ¿Necesitas más? Contáctanos para límites mayores.

FAQ

Preguntas frecuentes

¿Qué es la API de AI Emaily?

Una API REST versionada (api.aiemaily.com/v1) que expone tu bandeja de entrada unificada — Gmail, Outlook/Microsoft 365 y cualquier proveedor IMAP — más la capa de IA de AI Emaily (búsqueda semántica, redacción con tu voz, el Living Brief, el agente autónomo) a tus propios scripts, backends y herramientas de automatización.

¿Cómo obtengo una clave de API?

Ajustes → Desarrollador → Claves de API en la aplicación AI Emaily (plan Pro o superior). Las claves tienen ámbitos fijos al crearlas, se muestran una sola vez y se almacenan únicamente como hash. Usa una clave con ámbito reducido por integración.

¿Qué planes incluyen acceso a la API?

Pro ($20/mes) incluye la API REST con ámbitos básicos. Autopilot ($40/mes) añade ámbitos de agente y el servidor MCP. Team (desde $25/puesto) añade claves de organización y cuotas agrupadas. El plan Free no tiene acceso a la API.

¿Hay una especificación OpenAPI?

Sí — el documento OpenAPI 3.1 legible por máquinas se encuentra en api.aiemaily.com/v1/openapi.json y es la fuente de verdad del contrato completo. Los SDKs de TypeScript y Python se generan a partir de él, y puedes generar clientes para cualquier otro lenguaje.

¿Cómo se mantienen seguros los webhooks?

Cada entrega está firmada: el encabezado X-Aiemaily-Signature lleva una marca de tiempo y un HMAC-SHA256 del payload con el secreto de tu endpoint. Verifica la firma y rechaza marcas de tiempo antiguas (de más de 5 minutos) para prevenir ataques de repetición. Los secretos rotan con una ventana de validez doble de 24 horas.

¿Qué ocurre si supero un límite de uso?

Recibirás un 429 con un encabezado Retry-After. Se aplica un techo de ráfaga por minuto (rate_limited) por clave, por usuario y por IP; las cuotas diarias de peticiones y envíos (quota_exceeded) las fija el plan. Consulta GET /v1/usage (o los encabezados X-RateLimit en cualquier respuesta) para gestionar trabajos por lotes.

¿Puede la API saltarse la configuración de seguridad de la aplicación?

No. La API utiliza el mismo núcleo de cumplimiento con ámbito de propiedad que la aplicación y el servidor MCP: los envíos requieren confirmación explícita y cuentan contra los límites diarios, las ejecuciones del agente respetan tus modos de autoridad y listas de remitentes permitidos, y cada mutación queda auditada.

¿Se usa el contenido de mi correo para entrenar modelos?

Nunca. Acuerdos de retención cero con los proveedores de modelos y sin entrenamiento sobre el correo del usuario — la misma política de privacidad que rige la aplicación cubre cada llamada a la API y al servidor MCP.

Versionado y registro de cambios

Un contrato en el que puedes construir

Versión principal en la URL (/v1). Los cambios aditivos — nuevos endpoints, nuevos campos opcionales — se publican continuamente y nunca rompen clientes existentes. Los cambios disruptivos pasan a /v2 con al menos 6 meses de aviso de deprecación con ejecución dual, anunciados aquí y por correo a todos los propietarios de claves activas.

v1.0.0

2026 — GAactual
  • Lanzamiento público inicial: 21 herramientas en lectura, búsqueda & IA, organización, redacción & envío, y contexto & análisis.
  • JSON-RPC HTTP con streaming en https://mcp.aiemaily.com, negociando el protocolo MCP 2025-11-25; las herramientas llevan anotaciones (indicadores readOnly / destructive / idempotent).
  • OAuth 2.1 con PKCE y registro dinámico de clientes — la pantalla de consentimiento muestra una insignia de verificado/no verificado y el host de redirección, y limita ámbitos privilegiados; portador de clave de API (aiem_live_…) para clientes sin cabecera.
  • Herramientas de búsqueda y recuperación compatibles con ChatGPT, para que los conectores de investigación profunda y de conocimiento empresarial funcionen sin configuración adicional.
  • Envío en dos pasos con confirmación y ventana de deshacer en el servidor; delimitación de contenido no fiable en todas las salidas derivadas del correo.
  • Límites de uso por clave, por usuario y por IP, cuotas diarias de peticiones/envíos y registro de auditoría completo.

v0.9.0

2026 — beta privada
  • Beta con socios de diseño sobre la API REST v1.
  • Se añadieron get_attachment_text y list_agent_actions a partir del feedback de la beta.
  • Pantalla de consentimiento reescrita en lenguaje de producto (scope → inglés sencillo).

Construye sobre tu bandeja de entrada.

Crea una clave, haz una llamada, publica algo. El acceso a la API comienza en Pro; los agentes tienen el servidor MCP en Autopilot.