# EmailFlow AI > AI-native email marketing platform: audiences (lists, subscribers, segments), > campaigns, automations, AI email generation, sending domains, webhooks, and > analytics — all available over a scoped REST API. Authoritative resources: - OpenAPI 3.1 spec (machine-readable contract): https://emailflow.ai/openapi/public-api-v1.json (YAML: https://emailflow.ai/openapi/public-api-v1.yaml) - Human docs: https://emailflow.ai/docs (developer section: https://emailflow.ai/docs/api) - Full docs corpus for agents: https://emailflow.ai/llms-full.txt - TypeScript SDK: @emailflow/sdk (sdk/typescript in the repository) - API base URL: https://emailflow.ai/api/v1 ## Authentication Create scoped API keys under Account -> API (https://emailflow.ai/rui/account/api). Keys look like `efa_` + 40 base62 characters and are shown ONCE at creation — only a sha256 hash is stored. Send the key on every request using either header form: Authorization: Bearer efa_your_key_here X-API-Key: efa_your_key_here Both forms are equivalent. Keys can expire and be revoked; a revoked or expired key returns 401 `INVALID_API_KEY`. ## Scopes Grant only what an integration needs. Broad scopes include their narrower scopes (the Includes column); granular scopes grant only themselves. A key missing the required scope gets 403 `INSUFFICIENT_PERMISSIONS`. Scope Grants Includes ----------- ---------------------------------------------------- ------------------------- all Everything, including admin endpoints every scope contacts Lists and subscribers; includes Audiences audiences audiences Read audience segments - emails Email content; includes Domains, Sends and Templates domains, sends, templates domains Sending domains - sends Campaigns and sending - templates Email templates - automations Automation workflows; includes Triggers triggers triggers Trigger automation runs - webhooks Webhook subscriptions - analytics Event analytics feed - usage Usage and quota meters - brand Brand profile - ## Idempotency Unsafe requests (POST/PATCH/PUT/DELETE) accept an optional `Idempotency-Key` header (any string up to 255 chars; UUIDs recommended). Within a 24-hour window, replaying the same key + identical payload returns the ORIGINAL response with `Idempotency-Replayed: true` instead of re-executing — e.g. a replayed `POST /emails/generate` returns the same `template_uid` without generating (or charging) twice. The same key with a DIFFERENT payload returns 409 `CONFLICT`, as does a concurrent in-flight request with the same key. 5xx responses are not recorded, so retries after a server error re-execute. ## Rate limits Per API key (legacy-token callers share a per-user bucket). Successful responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`; a 429 carries `Retry-After` (seconds) — honor it and retry with backoff. Class Limit Applies to --------- ------- ------------------------------------------------------------- api.read 100/min All read (GET) requests on resource endpoints. api.write 60/min Create, update, and delete requests on resource endpoints. api.batch 10/min Bulk subscriber import (sync and async) and batch operations. api.ai 20/min AI email generation and edit requests. api.sends 10/min Campaign run (send) triggers. Untagged legacy endpoints share an outer 60/min guard. ## Errors Every error is a uniform JSON envelope: {"error": {"code", "type", "message", "suggestion"?, "docs"?, "param"?, "details"?, "retryAfter"?}} Cross-tenant and unknown uids both return 404 `NOT_FOUND` — existence is never leaked. HTTP Code Type What to do ---- ------------------------ --------------------- --------------------------------------------------------------------------------------------------- 400 INVALID_REQUEST invalid_request_error Check the request method, path, and parameters against the API reference. 401 INVALID_API_KEY authentication_error Create a new key under Account -> API, or check that the key was copied in full. 403 INSUFFICIENT_PERMISSIONS permission_error Use a key whose scopes cover this endpoint, or create one under Account -> API. 404 NOT_FOUND invalid_request_error Check the identifier; the resource may have been deleted or may belong to another account. 422 VALIDATION_ERROR invalid_request_error Fix the fields listed in details and retry the request. 429 RATE_LIMITED rate_limit_error Slow down and retry after the number of seconds given in retryAfter. 409 IDEMPOTENCY_CONFLICT invalid_request_error Use a fresh Idempotency-Key for a different request body, or replay the identical body. 402 CREDITS_EXHAUSTED billing_error Upgrade your plan or wait for the next billing cycle to refresh your credits. 402 PLAN_LIMIT billing_error Upgrade your plan to raise this limit. 409 CONFLICT invalid_request_error The resource is in a state that does not allow this operation; fetch it again and check its status. 500 SERVER_ERROR api_error Retry later; if the problem persists, contact support with the request timestamp. ## Resources Base URL: https://emailflow.ai/api/v1 — full request/response shapes live in the OpenAPI spec. ### Lists (`contacts`) CRUD at `/lists`; add custom fields with `POST /lists/{uid}/add-field`. Lists are the container every subscriber, segment, and automation hangs off. ### Subscribers (`contacts`) CRUD at `/subscribers` (+ `?list_uid=` filter); per-list status transitions under `/lists/{list_uid}/subscribers/...`; tag with `add-tag`/`remove-tag`. Custom-field values travel as UPPERCASE tag keys (e.g. `FIRST_NAME`). ### Bulk contacts (`contacts`) Up to 1,000 rows per call: `POST /lists/{uid}/subscribers/bulk` (sync upsert), `DELETE .../bulk` (bulk delete), `POST .../bulk/async` (import job; poll `GET /import-jobs/{uid}`). ### Segments (`contacts`) Condition-based audiences: `/lists/{uid}/segments` + `/segments/{uid}`; preview membership with `GET /segments/{uid}/subscribers`. Operators are validated against the live catalog (see the OpenAPI `SegmentCondition` schema). ### Campaigns (`campaigns`, send via `sends`) CRUD at `/campaigns`; `POST /campaigns/{uid}/run` queues the send (scope `sends`); `pause`/`resume`; CSV logs at `/campaigns/{uid}/{tracking,open,click,bounce,feedback,unsubscribe}-log/download`. ### AI email generation (`emails`) `POST /emails/generate` (prompt -> saved template + HTML, ~15-60s, send an Idempotency-Key) and `PATCH /emails/{template_uid}` (natural-language edit with checkpoint). Templates open in the visual builder afterwards. ### Templates (`templates`) Read-only: `GET /templates` (filter `search`, `source=gallery|ai|imported|api`), `GET /templates/{uid}?include=html`. ### Automations (`automations`) Graph CRUD at `/automations` (create from a validated graph OR an AI `prompt`); atomic batch edits via `PATCH` operations; `activate`/`deactivate`; fire per-contact runs with `POST /automations/{uid}/runs`; cancel via `PATCH /automation-runs/{uid}`. ### Sending domains (`domains`) `POST /domains` registers and returns the DNS records to publish; `POST /domains/{uid}/verify` re-checks DNS; deletion is blocked 409 while scheduled/sending campaigns use the domain. ### Webhooks (`webhooks`) `POST /webhooks` subscribes an https endpoint to events (secret returned once); deliveries are HMAC-signed (`X-EmailFlow-Signature: t=...,v1=...`), retried [30s, 5m, 30m], auto-disabled after 20 consecutive failures; `POST /webhooks/{uid}/test` pings synchronously. ### Brand (`brand`) Read-only `GET /brand`: company profile, colors, fonts, logo URL, and `crawl_status` to poll after a brand scan. ### Usage (`usage`) Read-only `GET /usage`: plan quota meters (used/limit/unlimited/granted) and credit snapshot (sends, AI tokens, verifications). ### Analytics (`analytics`) `GET /analytics/events`: unified feed of sent/failed/open/click/bounce/feedback/unsubscribe events, cursor-paginated, filterable by type/campaign/email/time range. ## Canonical examples Generate an email from a prompt: curl -X POST https://emailflow.ai/api/v1/emails/generate \ -H "Authorization: Bearer efa_your_key_here" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed" \ -d '{"prompt": "Welcome email for a coffee subscription, warm tone"}' Create a segment of recently-engaged contacts: curl -X POST https://emailflow.ai/api/v1/lists/LIST_UID/segments \ -H "X-API-Key: efa_your_key_here" \ -H "Content-Type: application/json" \ -d '{"name": "Engaged 30d", "matching": "all", "conditions": [{"type": "last_open_email", "operator": "last_open_email_less_than_days", "value": "30"}]}' Register a sending domain (then publish the returned DNS records and verify): curl -X POST https://emailflow.ai/api/v1/domains \ -H "Authorization: Bearer efa_your_key_here" \ -H "Content-Type: application/json" \ -d '{"domain": "mail.example.com"}' Fire an automation for one contact: curl -X POST https://emailflow.ai/api/v1/automations/AUTOMATION_UID/runs \ -H "Authorization: Bearer efa_your_key_here" \ -H "Content-Type: application/json" \ -d '{"email": "person@example.com"}' Register a webhook (store the returned secret — it is shown once): curl -X POST https://emailflow.ai/api/v1/webhooks \ -H "Authorization: Bearer efa_your_key_here" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/hooks/emailflow", "events": ["email.delivered", "contact.subscribed"]}'