Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Forward Email Messages API
description: 'Welcome to the comprehensive API documentation for Forward Email. This API allows you to manage email forwarding, domains, aliases, outbound emails, and more programmatically.
## Key Features
* **Domain Management:** Create, retrieve, update, delete, and verify domains.
* **Alias Management:** Manage email aliases, including creation, updates, and recipient management.
* **Outbound SMTP:** Send emails via SMTP, manage limits, and track sent emails.
* **Account & Logs:** Manage your account details and download activity logs.
* **Security:** Encrypt TXT records and manage domain members/invites.
* **CardDAV Contacts:** Full CRUD operations for contact management with vCard support.
* **CalDAV Calendars:** Complete calendar management with timezone and color support.
* **IMAP/POP3 Messages:** Comprehensive message management with advanced search capabilities.
* **IMAP/POP3 Folders:** Full folder management including creation, renaming, and deletion.
## Authentication
Most endpoints require authentication using your **API Key**. Provide the API Key as the `username` in the Basic Authentication header, leaving the password empty.
```http
Authorization: Basic QVBJX1RPS0VOOg==
```
(Replace `API_TOKEN` with your actual key before Base64 encoding)
Endpoints related to **Alias Contacts, Calendars, Messages, and Folders** require a generated alias username and password for Basic Authentication.
## Errors
The API uses standard HTTP status codes to indicate success or failure. Error responses include a JSON body with a `message` field detailing the error.
| Status Code | Meaning |
|-------------|---------------------|
| 200 | OK |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 409 | Conflict |
| 412 | Precondition Failed |
| 429 | Too Many Requests |
| 500 | Internal Server Error|
| 501 | Not Implemented |
| 502 | Bad Gateway |
| 503 | Service Unavailable |
| 504 | Gateway Time-out |
*If you encounter a `5xx` error, please contact api@forwardemail.net.*
## Localization
API responses are translated based on the user''s detected locale or the `Accept-Language` header. Over 25 languages are supported.
## Pagination
Endpoints returning lists support pagination via query parameters.
| Parameter | Type | Optional | Description | Default | Constraints |
|--------------|---------|----------|-----------------------------------------------------------------------------|---------|--------------------|
| `page` | Integer | Yes | Page number to retrieve. | 1 | `>= 1` |
| `limit` | Integer | Yes | Number of results per page. | 25 | `>= 1`, `<= 100` |
| `pagination` | Boolean | Yes | Opt-in to pagination behavior (required before Nov 1st, 2024 for some endpoints). | false | |
**Pagination Headers:**
* `X-Page-Count`: Total page count.
* `X-Page-Current`: Current page number.
* `X-Page-Size`: Number of items on the current page.
* `X-Item-Count`: Total number of items across all pages.
* `Link`: Navigation links (prev, next, first, last).
**Example:**
```bash
curl "https://api.forwardemail.net/v1/domains?page=2&limit=20&pagination=true" \
-u API_TOKEN:
```
## Recommended Libraries
* **Ruby:** Faraday
* **Python:** requests
* **Java:** OkHttp
* **PHP:** Guzzle
* **JavaScript/Node.js:** superagent (Maintained by Forward Email)
* **Go:** `net/http`
* **.NET:** RestSharp'
version: 1.0.0
contact:
name: Forward Email Support
url: https://forwardemail.net
email: api@forwardemail.net
servers:
- url: https://api.forwardemail.net
description: Forward Email API Server
tags:
- name: Messages
description: 'Alias messages management endpoints (IMAP/POP3)
>[!NOTE]
> Unlike other API endpoints, these require Authentication "username" equal to the alias username and "password" equal to the alias generated password as Basic Authorization headers.
>[!TIP]
> These endpoints provide comprehensive message management with advanced search capabilities. You can search by folder, flags, content, headers, date ranges, size, and more. Messages can be created using standard Nodemailer format and support moving between folders and flag management.
Please ensure that you have followed setup instructions for your domain.
These instructions can be found in our FAQ section Do you support receiving email with IMAP?.'
paths:
/v1/messages:
get:
tags:
- Messages
summary: List and search messages
description: Retrieve a list of messages with advanced search and filtering capabilities. This endpoint supports pagination and comprehensive search across message content, headers, flags, and metadata. You can search by folder, flags, content, headers, date ranges, size, and more.
operationId: listMessages
security:
- AliasAuth: []
parameters:
- $ref: '#/components/parameters/page'
- $ref: '#/components/parameters/limit'
- name: folder
in: query
description: Filter messages by folder path
schema:
type: string
- name: is_unread
in: query
description: Filter by read status
schema:
type: boolean
- name: is_flagged
in: query
description: Filter by flagged status
schema:
type: boolean
- name: is_deleted
in: query
description: Filter by deleted status
schema:
type: boolean
- name: is_draft
in: query
description: Filter by draft status
schema:
type: boolean
- name: is_junk
in: query
description: Filter by junk/spam status
schema:
type: boolean
- name: is_copied
in: query
description: Filter by copied status
schema:
type: boolean
- name: is_encrypted
in: query
description: Filter by encryption status (PGP/MIME or S/MIME)
schema:
type: boolean
- name: is_searchable
in: query
description: Filter by searchable status
schema:
type: boolean
- name: is_expired
in: query
description: Filter by expired status
schema:
type: boolean
- name: has_attachments
in: query
description: Filter by attachment presence
schema:
type: boolean
- name: has_attachment
in: query
description: Filter by attachment presence (alias for has_attachments)
schema:
type: boolean
- name: subject
in: query
description: Search in message subject
schema:
type: string
- name: body
in: query
description: Search in message body/text
schema:
type: string
- name: text
in: query
description: Search in message text (alias for body)
schema:
type: string
- name: headers
in: query
description: Search in all message headers
schema:
type: string
- name: message_id
in: query
description: Search by Message-ID header
schema:
type: string
- name: search
in: query
description: General search across headers and text
schema:
type: string
- name: q
in: query
description: General search (alias for search)
schema:
type: string
- name: since
in: query
description: Filter messages since this date (ISO 8601 format)
schema:
type: string
format: date-time
- name: before
in: query
description: Filter messages before this date (ISO 8601 format)
schema:
type: string
format: date-time
- name: min_size
in: query
description: Filter messages with minimum size in bytes
schema:
type: integer
- name: max_size
in: query
description: Filter messages with maximum size in bytes
schema:
type: integer
- name: from
in: query
description: Search by From header
schema:
type: string
- name: to
in: query
description: Search by To header
schema:
type: string
- name: cc
in: query
description: Search by CC header
schema:
type: string
- name: bcc
in: query
description: Search by BCC header
schema:
type: string
- name: date
in: query
description: Search by Date header
schema:
type: string
- name: reply-to
in: query
description: Search by Reply-To header
schema:
type: string
- name: lightweight
in: query
required: false
schema:
type: string
enum:
- 'true'
- 'false'
default: 'false'
description: When set to "true", returns metadata-only responses without nodemailer or raw fields. This significantly improves performance by skipping full MIME tree reconstruction and attachment body fetching. Useful for building message lists where only metadata (subject, flags, dates, folder, size) is needed.
responses:
'200':
description: Messages retrieved successfully
headers:
X-Page-Count:
description: Total number of pages
schema:
type: integer
X-Page-Current:
description: Current page number
schema:
type: integer
X-Page-Size:
description: Number of items on current page
schema:
type: integer
X-Item-Count:
description: Total number of items
schema:
type: integer
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
post:
tags:
- Messages
summary: Create message
description: Create/append a new message to a folder. This endpoint accepts standard Nodemailer message format and allows you to specify the target folder, initial flags, and labels. If the target folder doesn't exist, it will be created automatically.
operationId: createMessage
security:
- AliasAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MessageInput'
responses:
'201':
description: Message created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/messages/{id}:
get:
tags:
- Messages
summary: Retrieve message
description: Retrieve a specific message by its ID. By default, the response includes the parsed Nodemailer object and raw message content. You can control what's included using query parameters.
operationId: retrieveMessage
security:
- AliasAuth: []
parameters:
- $ref: '#/components/parameters/messageId'
- name: eml
in: query
description: Return raw EML format instead of JSON
schema:
type: boolean
default: false
- name: nodemailer
in: query
description: Include parsed Nodemailer object
schema:
type: boolean
default: true
- name: attachments
in: query
description: Include attachments in Nodemailer object
schema:
type: boolean
default: true
- name: raw
in: query
description: Include raw message content
schema:
type: boolean
default: true
responses:
'200':
description: Message retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
message/rfc822:
description: Raw EML format (when ?eml=true)
schema:
type: string
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Messages
summary: Update message
description: Update an existing message. You can modify the message flags, labels, or move it to a different folder. If the target folder doesn't exist, it will be created automatically.
operationId: updateMessage
security:
- AliasAuth: []
parameters:
- $ref: '#/components/parameters/messageId'
- name: eml
in: query
description: Return raw EML format instead of JSON
schema:
type: boolean
default: false
- name: lightweight
in: query
required: false
schema:
type: string
enum:
- 'true'
- 'false'
default: 'false'
description: When set to "true", returns metadata only, without nodemailer or raw fields. Skips rebuilding the full message, so flag and label changes respond quickly.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MessageUpdateInput'
responses:
'200':
description: Message updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
message/rfc822:
description: Raw EML format (when ?eml=true)
schema:
type: string
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- Messages
summary: Delete message
description: Delete a specific message permanently. This action cannot be undone and will permanently remove the message from storage. Unlike IMAP clients which typically move messages to Trash, this endpoint performs a hard delete.
operationId: deleteMessage
security:
- AliasAuth: []
parameters:
- $ref: '#/components/parameters/messageId'
responses:
'200':
description: Message deleted successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Message'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
components:
schemas:
Message:
type: object
required:
- id
- root_id
- folder_id
- folder_path
- thread_id
- header_message_id
- is_unread
- is_flagged
- is_deleted
- is_draft
- is_junk
- is_copied
- is_encrypted
- is_searchable
- is_expired
- has_attachment
- retention_date
- internal_date
- header_date
- subject
- flags
- labels
- size
- uid
- modseq
- transaction
- remote_address
- created_at
- updated_at
- object
properties:
id:
type: string
description: Message ID
root_id:
type: string
description: Root message ID for threading
folder_id:
type: string
description: Folder ID containing the message
folder_path:
type: string
description: Folder path containing the message
thread_id:
type: string
description: Thread ID for conversation grouping
header_message_id:
type: string
description: Message-ID header value
is_unread:
type: boolean
description: Whether message is unread
is_flagged:
type: boolean
description: Whether message is flagged
is_deleted:
type: boolean
description: Whether message is deleted
is_draft:
type: boolean
description: Whether message is a draft
is_junk:
type: boolean
description: Whether message is junk/spam
is_copied:
type: boolean
description: Whether message is copied
is_encrypted:
type: boolean
description: Whether message is encrypted (PGP/MIME or S/MIME)
is_searchable:
type: boolean
description: Whether message is searchable
is_expired:
type: boolean
description: Whether message is expired
has_attachment:
type: boolean
description: Whether message has attachments
retention_date:
type:
- string
- 'null'
format: date-time
description: Message retention date
internal_date:
type: string
format: date-time
description: Internal date (IMAP)
header_date:
type: string
format: date-time
description: Date from message headers
subject:
type: string
description: Message subject
flags:
type: array
items:
type: string
description: IMAP flags
labels:
type: array
items:
type: string
description: User-defined labels (lowercase, max 10)
example:
- work
- important
- follow-up
size:
type: integer
description: Message size in bytes
uid:
type: integer
description: IMAP UID
modseq:
type: integer
description: Modification sequence
transaction:
type: string
description: Transaction type
remote_address:
type: string
description: Remote IP address
created_at:
type: string
format: date-time
description: Message creation timestamp
updated_at:
type: string
format: date-time
description: Message last update timestamp
nodemailer:
type: object
description: Parsed message object (included by default, set ?nodemailer=false to exclude)
raw:
type: string
description: Raw message content (included by default, set ?raw=false to exclude)
object:
type: string
enum:
- message
example:
id: message123
root_id: root123
folder_id: folder123
folder_path: INBOX
thread_id: thread123
header_message_id: <message123@example.com>
is_unread: true
is_flagged: false
is_deleted: false
is_draft: false
is_junk: false
is_copied: false
is_encrypted: false
is_searchable: true
is_expired: false
has_attachment: false
retention_date: null
internal_date: '2025-01-01T00:00:00.000Z'
header_date: '2025-01-01T00:00:00.000Z'
subject: Test Message
flags:
- \Seen
size: 1024
uid: 1
modseq: 1
transaction: API
remote_address: 127.0.0.1
created_at: '2025-01-01T00:00:00.000Z'
updated_at: '2025-01-01T00:00:00.000Z'
object: message
MessageInput:
type: object
properties:
from:
type: string
format: email
description: Sender email address
to:
oneOf:
- type: string
- type: array
items:
type: string
format: email
description: Recipient email addresses
cc:
oneOf:
- type: string
- type: array
items:
type: string
format: email
description: CC recipient email addresses
bcc:
oneOf:
- type: string
- type: array
items:
type: string
format: email
description: BCC recipient email addresses
subject:
type: string
description: Message subject
text:
type: string
description: Plain text content
html:
type: string
description: HTML content
attachments:
type: array
items:
type: object
description: Message attachments (Nodemailer format)
folder:
type: string
description: Target folder (defaults to INBOX)
flags:
oneOf:
- type: string
- type: array
items:
type: string
description: IMAP flags to set on the message
labels:
oneOf:
- type: string
- type: array
items:
type: string
description: User-defined labels to set on the message. Can be a single label string or an array of labels. Labels must start with a letter, digit, backslash, or dollar sign, and can contain word characters, dots, and hyphens. Maximum of 10 labels per message. Labels are case-insensitive and will be stored in lowercase.
example:
- work
- important
Error:
type: object
required:
- message
properties:
message:
type: string
description: Error message
MessageUpdateInput:
type: object
properties:
flags:
type: array
items:
type: string
description: IMAP flags to set on the message
flags_add:
type: array
items:
type: string
description: IMAP flags to add to the message. Its other flags are kept, so changes another client made in the meantime are not undone. Takes precedence over flags.
example:
- \Seen
flags_remove:
type: array
items:
type: string
description: IMAP flags to remove from the message. A keyword removed here is removed from its labels as well. Its other flags are kept. Takes precedence over flags.
example:
- \Seen
labels:
oneOf:
- type: string
- type: array
items:
type: string
description: User-defined labels to set on the message. Can be a single label string or an array of labels. Labels must start with a letter, digit, backslash, or dollar sign, and can contain word characters, dots, and hyphens. Maximum of 10 labels per message. Labels are case-insensitive and will be stored in lowercase. Keywords mail clients use to track the state of a message, such as $Forwarded or $Junk, are kept unless removed with labels_remove.
example:
- work
- important
labels_add:
type: array
items:
type: string
description: Labels to add to the message. Its other labels are kept. Takes precedence over labels.
example:
- work
labels_remove:
type: array
items:
type: string
description: Labels to remove from the message, for IMAP clients as well (they see labels as keywords). Its other labels are kept. Takes precedence over labels.
example:
- work
folder:
type: string
description: Move message to this folder
responses:
NotFound:
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InternalServerError:
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
parameters:
page:
name: page
in: query
description: Page of results to return. If not specified, value will be 1. Must be a number greater than or equal to 1.
schema:
type: integer
minimum: 1
default: 1
messageId:
name: id
in: path
required: true
description: Message ID
schema:
type: string
limit:
name: limit
in: query
description: Number of results to return per page. Defaults to 25 if not specified. Must be a number greater than or equal to 1, and less than or equal to 100.
schema:
type: integer
minimum: 1
maximum: 100
default: 25
securitySchemes:
ApiKeyAuth:
type: http
scheme: basic
description: '
Use your API Key as the **username** for Basic Authentication. Leave the password field empty.
**Example:**
If your API Key is `YOUR_API_KEY`, the Base64 encoded value for the `Authorization` header would be `echo -n "YOUR_API_KEY:" | base64`.
```http
Authorization: Basic WU9VUl9BUElfS0VZOg==
```
'
AliasAuth:
type: http
scheme: basic
description: '
Use the generated **alias username and password** for Basic Authentication. This applies to endpoints under:
* Alias Contacts (CardDAV)
* Alias Calendars (CalDAV)
* Alias Messages (IMAP/POP3)
* Alias Folders (IMAP/POP3)
* Alias Sieve Scripts
**Example:**
If the alias username is `alias@domain.com` and password is `GENERATED_PASSWORD`, the Base64 encoded value would be `echo -n "alias@domain.com:GENERATED_PASSWORD" | base64`.
```http
Authorization: Basic YWxpYXNAZG9tYWluLmNvbTpHRU5FUkFURURfUEFTU1dPUkQ=
```
'