API
EmailFlow AI ships a clean, resource-oriented REST API on every plan. Authenticate with a single token, send and receive JSON, and manage lists, subscribers, campaigns, and automations programmatically — the same endpoints the app itself uses.
Base URL and versioning
Every endpoint lives under the /api/v1/ prefix on your EmailFlow AI host. The current major version is v1; new endpoints are added without breaking existing ones, and any breaking change would ship as a new version alongside v1.
https://emailflow.ai/api/v1/<resource>
emailflow.ai with your own hostname on a self-hosted install (for example a local development server at http://127.0.0.1:8000/api/v1/). The examples below use the hosted base URL.Authentication
Every request carries a single API token that is unique to your user and inherits your permissions. Generate it from your account: sign in, open your profile menu, and go to Account → API. Treat the token like a password — if it is ever exposed, rotate it from the same screen.
Pass the token in one of two ways. The Authorization header is preferred; the ?api_token= query parameter is supported for legacy clients.
# Preferred: bearer header curl https://emailflow.ai/api/v1/me \ -H "Authorization: Bearer YOUR_API_TOKEN" # Legacy: query parameter curl "https://emailflow.ai/api/v1/me?api_token=YOUR_API_TOKEN"
If you only have an email and password (for example from a programmatic client), exchange them for a token with POST /user/login, documented under Authentication and identity below. A missing or invalid token returns 401 Unauthorized.
Requests, responses, and errors
Send parameters as form fields or, where noted, a JSON body. Every response is JSON. Status codes follow REST conventions, so you can branch your client on the code alone.
| Code | Meaning |
|---|---|
| 200 OK | Successful read, update, or action. |
| 201 Created | Resource created. |
| 202 Accepted | Work queued (for example an asynchronous bulk import). |
| 401 Unauthorized | Missing or invalid token. |
| 403 Forbidden | Token valid, but the action is not permitted (validation failure or quota limit). |
| 404 Not Found | The requested resource does not exist. |
| 500 Server error | Something went wrong on the server. |
Pagination. Index endpoints accept page and per_page (defaults vary by resource — 10 for lists and campaigns, 25 for subscribers). Pass them as query parameters to walk large collections.
Validation errors. When input fails validation the response carries a 403 with a field-keyed map of messages:
{
"from_email": [
"The from email must be a valid email address."
]
}
Authentication and identity
Endpoints for obtaining a token, verifying it, and reading the current account.
| Method | Endpoint | Description |
|---|---|---|
| POST | /user/login | Exchange email and password for an API token. Public — no token required. |
| GET | /me | Return the authenticated user record. Use it to verify a token. |
| GET | /user/info | Return the user profile: email, first and last name, and profile image URL. |
| GET | /dashboard | Return account summary counts: campaigns, lists, and subscriber usage against quota. |
| POST | /login-token | Generate a one-time URL that logs the user in when visited. |
# Get a token from email + password
curl -X POST https://emailflow.ai/api/v1/user/login \
-d email=you@example.com \
-d password=YOUR_PASSWORD
# Response
{ "api_token": "9f2c1ab4e5..." }
Lists
Mailing lists are the primary container for subscribers. Each list has a default sender identity and a set of custom fields, which you add with the add-field sub-route.
| Method | Endpoint | Description |
|---|---|---|
| GET | /lists | Paginated index of lists owned by the token's user. |
| POST | /lists | Create a list. Required: name, from_email, from_name. |
| GET | /lists/{uid} | Fetch a single list with its fields and statistics. |
| POST | /lists/{uid}/add-field | Add a custom field. type is one of text, number, datetime. |
| DELETE | /lists/{uid} | Permanently delete a list and its subscribers. |
# Create a list
curl -X POST https://emailflow.ai/api/v1/lists \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d name="Newsletter" \
-d from_name="Acme" \
-d from_email="hi@acme.com" \
-d contact[company]="Acme Inc." \
-d contact[address_1]="1 Market St" \
-d contact[city]="San Francisco" \
-d contact[zip]="94105" \
-d contact[phone]="+1 555 0100" \
-d contact[country_id]=1 \
-d contact[email]="hi@acme.com"
# Response
{
"status": 1,
"message": "List has been created!",
"list_uid": "ab12cd34ef"
}
Add a custom field to that list:
curl -X POST https://emailflow.ai/api/v1/lists/{uid}/add-field \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d type=text \
-d label="First name" \
-d tag=FIRST_NAME
Subscribers
Subscribers belong to a list and carry custom field values, tags, and a status of subscribed, unsubscribed, or unconfirmed. Custom fields are passed as uppercase parameters (FIRST_NAME, LAST_NAME, …) matching your list's field configuration.
Single subscriber operations
| Method | Endpoint | Description |
|---|---|---|
| GET | /subscribers?list_uid={uid} | Paginated index for a list. Filter with open=yes|no and click=yes|no. |
| POST | /subscribers | Create a subscriber. Required: list_uid, EMAIL. |
| GET | /subscribers/{id} | Fetch a single subscriber by id or email. |
| PATCH | /subscribers/{id} | Update fields, tags, or status. |
| POST | /subscribers/{id}/add-tag | Add comma-separated tags. |
| POST | /subscribers/{id}/remove-tag | Remove comma-separated tags. |
| GET | /subscribers/email/{email} | Find every list containing a subscriber with this email. |
| PATCH | /lists/{list_uid}/subscribers/{id}/subscribe | Mark the subscriber subscribed. |
| PATCH | /lists/{list_uid}/subscribers/{id}/unsubscribe | Mark the subscriber unsubscribed. |
| PATCH | /lists/{list_uid}/subscribers/email/{email}/unsubscribe | Unsubscribe by email when you do not have the id. |
| DELETE | /subscribers/{id} | Permanently delete the subscriber from the list. |
# Add a subscriber with custom fields and tags curl -X POST https://emailflow.ai/api/v1/subscribers \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d list_uid=ab12cd34ef \ -d EMAIL=alice@example.com \ -d FIRST_NAME=Alice \ -d tag=vip,newsletter \ -d status=subscribed
Bulk import
Import many subscribers at once. The synchronous endpoint applies rows inline and returns per-row results — ideal for small batches. The asynchronous endpoint queues the work and returns a job you poll for progress — ideal for large files. Write endpoints are rate-limited to 60 requests per minute.
| Method | Endpoint | Description |
|---|---|---|
| POST | /lists/{list_uid}/subscribers/bulk | Synchronous bulk import. Returns counts and per-row errors. |
| POST | /lists/{list_uid}/subscribers/bulk/async | Queued bulk import. Accepts a JSON body or a multipart CSV upload. Returns 202 with a job. |
| GET | /lists/{list_uid}/import-jobs | List recent bulk-import jobs for a list. |
| GET | /import-jobs/{job_uid} | Poll a job for status, progress, and counts. |
| DELETE | /import-jobs/{job_uid} | Cancel a queued or running job. |
# Synchronous bulk import (JSON body)
curl -X POST "https://emailflow.ai/api/v1/lists/{list_uid}/subscribers/bulk" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "overwrite",
"subscribers": [
{ "EMAIL": "alice@example.com", "FIRST_NAME": "Alice", "tags": ["vip"] },
{ "EMAIL": "bob@example.com", "FIRST_NAME": "Bob", "status": "subscribed" }
]
}'
# Response
{
"list_uid": "ab12cd34ef",
"received": 2,
"inserted": 1,
"updated": 1,
"failed": 0,
"batch_id": "imp_9f2c1a",
"errors": []
}
For large or untrusted inputs, queue the job and poll it:
# Queue an async import, then poll
curl -X POST "https://emailflow.ai/api/v1/lists/{list_uid}/subscribers/bulk/async" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-F "file=@subscribers.csv" \
-F 'mapping={"Email":"EMAIL","First Name":"FIRST_NAME"}' \
-F "mode=overwrite"
# 202 Accepted
{ "job_uid": "job_77aa", "status": "queued", "status_url": "...", "cancel_url": "..." }
# Poll
curl "https://emailflow.ai/api/v1/import-jobs/job_77aa" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Campaigns
Campaigns are the unit of sending. Create one with HTML content and tracking flags, move it through run / pause / resume, then download per-event logs as CSV. See the campaigns guide for the full picture.
Managing campaigns
| Method | Endpoint | Description |
|---|---|---|
| GET | /campaigns | Paginated index. Accepts page and per_page. |
| POST | /campaigns | Create a campaign. Required: list_uid, name, subject, from_email, from_name. |
| GET | /campaigns/{uid} | Fetch a single campaign with statistics. |
| PATCH | /campaigns/{uid} | Update content, subject, or tracking flags. |
| POST | /campaigns/{uid}/run | Queue the campaign for delivery. |
| POST | /campaigns/{uid}/pause | Pause an in-flight campaign. |
| POST | /campaigns/{uid}/resume | Resume a paused campaign. |
| DELETE | /campaigns/{uid} | Delete a campaign that is not in progress. |
# Create then run a campaign
curl -X POST https://emailflow.ai/api/v1/campaigns \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d list_uid=ab12cd34ef \
-d name="Spring sale" \
-d subject="20% off this week" \
-d from_email=hi@acme.com \
-d from_name="Acme" \
-d track_open=true \
-d track_click=true \
--data-urlencode 'html=<p>Hello!</p>'
curl -X POST https://emailflow.ai/api/v1/campaigns/{uid}/run \
-H "Authorization: Bearer YOUR_API_TOKEN"
Log downloads
Each log endpoint streams a CSV for one event type. See reports and logs for how this data maps to the in-app reports.
| Method | Endpoint | Description |
|---|---|---|
| GET | /campaigns/{uid}/tracking-log/download | Combined tracking log. |
| GET | /campaigns/{uid}/open-log/download | Opens. |
| GET | /campaigns/{uid}/click-log/download | Clicks. |
| GET | /campaigns/{uid}/bounce-log/download | Bounces. |
| GET | /campaigns/{uid}/feedback-log/download | Spam and abuse feedback. |
| GET | /campaigns/{uid}/unsubscribe-log/download | Unsubscribes. |
Automations
Automations are visual flows of triggers, conditions, and actions. The API lets you list your automations and fire one for a contact from an external event — pair it with the API trigger node to start a flow from your own system.
| Method | Endpoint | Description |
|---|---|---|
| GET | /automations | Paginated index of automations the token's user owns. |
| POST | /automations/{uid}/execute | Trigger an automation. Useful for transactional flows. |
curl -X POST https://emailflow.ai/api/v1/automations/{uid}/execute \
-H "Authorization: Bearer YOUR_API_TOKEN"
# Response
{ "success": true }
Notifications
If you operate your own delivery pipeline, push delivery feedback back into EmailFlow AI so reports and suppression stay accurate. Report hard bounces and spam or abuse complaints against the original message id.
| Method | Endpoint | Description |
|---|---|---|
| POST | /notification/bounce | Record a hard bounce. Parameters: message_id, description. |
| POST | /notification/feedback | Record a feedback report. Parameters: message_id, type (spam or abuse), description. |
curl -X POST https://emailflow.ai/api/v1/notification/bounce \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d message_id="2016374...@example.com" \ -d description="550 mailbox not found"
Frequently asked questions
Do I need the API to use EmailFlow AI? No — everything works from the app. The API is for teams who want to connect their own apps and systems and automate data flow.
Which token format should I use? Prefer the Authorization: Bearer header. The ?api_token= query parameter still works for legacy clients but may appear in logs, so avoid it for sensitive calls.
How do I keep my keys safe? Treat API tokens like passwords: store them as secrets, never commit them to code or client-side bundles, use a separate token per integration, and rotate any token that is ever exposed from Account → API.
Are there rate limits? Bulk subscriber write endpoints are limited to 60 requests per minute. Other endpoints run against your own send and quota limits — see plans and quotas.