Authentication & Conventions
Use header-based authentication only: Authorization: Bearer <key> or X-Api-Key: <key>.
Base URL
https://mailapi.tech/api
API Key Format
32 characters (alpha-numeric)
Queue Behavior
Work that runs in the background returns a `task_id`. Poll the matching get-task endpoint for that operation (see the Endpoints list) until `status` is `completed` or `failed`. Bulk domain deletes accept up to 10 hostnames per request; bulk mailbox deletes accept up to 100 addresses; both follow the same task pattern. Each bulk-delete type allows only one successfully queued request every 5 minutes for a given API key and client IP together; validation errors (HTTP 422) do not count toward that limit. Creating resources and running bulk deletes requires an active pricing plan (admin accounts are exempt).
Success Contract
{
"status": "success"
}
Error Contract
{
"status": "error",
"reason": "Error description"
}
POST
/api/v1/create/domain
Create Domain
Creates a new base domain in your MailAPI workspace and queues provisioning in the background.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| Domain |
string |
Yes |
Fully qualified hostname (e.g. example.com). Omit http(s)://; single-label names are rejected (same rules as the dashboard Add domain form). |
| Params |
object |
No |
Optional keys (for example `total_mailbox_allowed`). |
| Params.total_mailbox_allowed |
integer |
No |
Mailbox slots recorded for the domain (minimum 1, maximum 100; defaults to the app maximum per hostname). |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/create/domain') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Domain": "example.com",
"Params": {
"total_mailbox_allowed": 100
}
}'
Success Response
{
"status": "success",
"domain": "example.com",
"task_id": 101
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create domains, subdomains, or mailboxes via the API.
-
HTTP 409 - Domain already exists for this account.
-
HTTP 422 - First validation error message (invalid body fields).
Notes
- Invalid hostnames return HTTP 422 before enqueue; duplicates return HTTP 409.
- Poll `GET /api/v1/get/domain?task_id=…`. When `completed`, `results` includes DNS `records` (MX/TXT/A).
- Remote mail server defaults apply on provision; quota fields are not accepted on this request.
GET
/api/v1/get/domain?task_id={task_id}
Get Domain Task
Polls a root domain create task created by `POST /api/v1/create/domain`.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by Create Domain. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/domain') }}?task_id=101" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 101,
"total_email": 0,
"domain": "example.com",
"results": {
"domain": "example.com",
"status": "Created successfully.",
"records": [
{
"type": "MX",
"name": "example.com",
"value": "mail.mailapi.tech",
"priority": "10",
"status": "Not checked"
},
{
"type": "TXT",
"name": "_mailapi.example.com",
"value": "<generated-unique-token>",
"priority": "\u2014",
"status": "Not checked"
},
{
"type": "A",
"name": "mail.example.com",
"value": "157.230.159.61",
"priority": "\u2014",
"status": "Not checked"
}
]
},
"status": "completed",
"task_id": "101"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from the queue job (same shape as other errors).
Notes
- `status` may be `queued`, `running`, `completed`, or `failed`. Only `failed` returns HTTP 422 with `reason` from the task.
- While `queued` or `running`, `results` is often an empty array until the job finishes.
- Responses include `Task_id` (integer) and `task_id` (same value as a string).
POST
/api/v1/create/sub-domain
Create Sub-Domain Batch
Creates up to 10 sub-domains under an existing parent domain in a single queued task.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| Domain |
string |
Yes |
Existing parent (root) domain owned by your account. Must be a valid dotted hostname (same rules as Create Domain / dashboard). |
| Params.sub_domains |
string[] |
Yes |
Sub-domain labels (not FQDN), max 10 per request. |
| Params.total_mailbox_allowed |
integer |
No |
Mailbox slots per created hostname (min 1, max 100; defaults to the app maximum per hostname). |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/create/sub-domain') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Domain": "example.com",
"Params": {
"sub_domains": ["team", "support", "billing"]
}
}'
Success Response
{
"status": "success",
"domain": "example.com",
"task_id": 202
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create domains, subdomains, or mailboxes via the API.
-
HTTP 404 - Parent domain was not found.
-
HTTP 422 - Params.sub_domains must be a non-empty array (or validation on labels).
-
HTTP 422 - Maximum 10 sub-domains are allowed per request.
-
HTTP 429 - Sub-domain requests are limited to once per 5 minutes for this domain and API key.
Notes
- After a successful run: at most one request per 5 minutes per API key and parent domain.
- Poll `GET /api/v1/get/sub-domain?task_id=…`. When `completed`, nested `results` are keyed by FQDN.
- Provisioning uses the same remote defaults as Create Domain; quota fields are not accepted.
GET
/api/v1/get/sub-domain?task_id={task_id}
Get Sub-Domain Task
Polls a sub-domain batch task from `POST /api/v1/create/sub-domain`.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by Create Sub-Domain. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/sub-domain') }}?task_id=202" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 202,
"total_email": 0,
"domain": "example.com",
"results": {
"domain": "example.com",
"results": {
"team.example.com": {
"status": "Created successfully.",
"records": [
{
"type": "MX",
"name": "team.example.com",
"value": "mail.mailapi.tech",
"priority": "10",
"status": "Not checked"
}
]
},
"support.example.com": {
"status": "Created successfully.",
"records": [
{
"type": "MX",
"name": "support.example.com",
"value": "mail.mailapi.tech",
"priority": "10",
"status": "Not checked"
}
]
}
}
},
"status": "completed",
"task_id": "202"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from provisioning.
Notes
- `status` values are the same as other task endpoints (`queued`, `running`, `completed`, `failed`).
- Responses include `Task_id` (integer) and `task_id` (same value as a string).
GET
/api/v1/list/domain
List Domains
Returns all domains for the API key owner account, keyed by domain name.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/list/domain') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"total_domain": 2,
"results": {
"example.com": {
"status": "verified",
"domain_type": "root",
"domain_quota": "0GB",
"mailbox_count": "15",
"created_at": "14-04-2026"
},
"team.example.com": {
"status": "verified",
"domain_type": "sub",
"domain_quota": "0GB",
"mailbox_count": "3",
"created_at": "15-04-2026"
}
},
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
Notes
- `domain_type` is `root` or `sub`.
- `domain_quota` is a string with a `GB` suffix; `0GB` means unlimited in list responses (non-zero may appear on older rows).
POST
/api/v1/create/mailbox/single
Create Single Mailbox
Queues one mailbox creation request for a domain that belongs to your account.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| Email_fullname |
string |
Yes |
Display name stored on the mailbox (not the email local-part). |
| single_bulk |
string |
Yes |
Must be `single`. |
| Domain |
string |
Yes |
Target domain hostname. |
| Params |
object |
Yes |
Payload object for mailbox provisioning details. |
| Params.email_id |
string |
Yes |
Mailbox address. Must belong to the provided Domain. |
| Params.password |
string|integer |
Yes |
Mailbox password string (minimum 8 characters) or numeric `0` to auto-generate a secure password (20+ chars). |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/create/mailbox/single') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Email_fullname": "John Doe",
"single_bulk": "single",
"Domain": "example.com",
"Params": {
"email_id": "john@example.com",
"password": 0
}
}'
Success Response
{
"status": "success",
"task_id": 123
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create domains, subdomains, or mailboxes via the API.
-
HTTP 404 - Domain not found for this account.
-
HTTP 422 - First validation error message (invalid body fields).
Notes
- Invalid bodies return HTTP 422 before enqueue. Only numeric `0` for `Params.password` triggers auto-generation (string `"0"` is rejected).
- Poll `GET /api/v1/get/mailbox/single?task_id=…`; remote failures return HTTP 422 on that poll.
POST
/api/v1/create/mailbox/bulk
Create Bulk Mailboxes
Queues a bulk mailbox provisioning task and returns a task ID for later retrieval.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| Email_fullname |
string |
Yes |
Display name used with internal rules to derive unique mailbox local-parts. |
| single_bulk |
string |
Yes |
Must be `bulk`. |
| Domain |
string |
Yes |
Target domain hostname. |
| Params |
object |
Yes |
Payload object for mailbox provisioning details. |
| Params.mailbox_no |
integer |
Yes |
How many mailboxes to create (must be between 1 and 100). |
| Params.password |
string|integer |
Yes |
Shared password string for the batch (minimum 8 characters), or numeric `0` to generate a unique secure password (20+ chars) per mailbox. |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/create/mailbox/bulk') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Email_fullname": "Team Member",
"single_bulk": "bulk",
"Domain": "example.com",
"Params": {
"mailbox_no": 25,
"password": 0
}
}'
Success Response
{
"status": "success",
"task_id": 456
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create domains, subdomains, or mailboxes via the API.
-
HTTP 404 - Domain not found for this account.
-
HTTP 422 - First validation error message (invalid body fields).
-
HTTP 429 - Bulk mailbox requests are limited to once per 5 minutes for this domain and API key.
Notes
- After a successful run: at most one bulk create per 5 minutes per API key and target hostname.
- `Params.mailbox_no` must be between 1 and 100.
- If a generated mailbox address already exists, it is skipped and the task continues creating the remaining addresses.
- If all requested addresses already exist, the task still completes successfully with `total_email` set to `0` and an empty `results` object.
- Only numeric `0` for `Params.password` triggers per-mailbox auto-generation (string `"0"` is rejected).
- Poll `GET /api/v1/get/mailbox/bulk?task_id=…`; remote failures return HTTP 422 on that poll.
GET
/api/v1/get/mailbox/single?task_id={task_id}
Get Single Mailbox Task
Fetches status and final result for a single mailbox provisioning task.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by create endpoint. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/mailbox/single') }}?task_id=123" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 123,
"total_email": 1,
"domain": "example.com",
"results": {
"john@example.com": {
"email": "john@example.com",
"password": "SecurePass123",
"quota": "0MB",
"domain": "example.com",
"provisioning": "pending",
"create_date": "2026-04-26 16:45:00"
}
},
"status": "completed",
"task_id": "123"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from `error_message` when the task fails.
Notes
- `status` is `queued`, `running`, `completed`, or `failed`. While pending, `results` is often empty.
- Both `Task_id` (int) and `task_id` (string) are returned with the same value.
- When `completed`, each entry includes `email`, `password`, `quota`, `domain`, `provisioning`, `create_date` (no per-entry `status`).
- If create used `Params.password: 0`, the response `password` is generated per mailbox.
GET
/api/v1/get/mailbox/bulk?task_id={task_id}
Get Bulk Mailbox Task
Fetches status and per-mailbox results for a bulk mailbox provisioning task.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by bulk create endpoint. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/mailbox/bulk') }}?task_id=456" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 456,
"total_email": 25,
"domain": "example.com",
"results": {
"team1@example.com": {
"status": "Created successfully.",
"password": "kL4!pQ9#uD2@xM7&zT8w"
},
"team2@example.com": {
"status": "Created successfully.",
"password": "rN6$hS1*eV0!cB3%yK5m"
}
},
"status": "completed",
"task_id": "456"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from `error_message` when the task fails.
Notes
- `status` follows the same lifecycle as single mailbox tasks.
- Responses include `Task_id` (integer) and `task_id` (same value as a string).
- When some requested addresses already exist, those addresses are skipped and `total_email` reflects only newly created mailboxes.
- If all requested addresses already exist, the task still completes successfully with `total_email` set to `0` and an empty `results` object.
- Results include mailbox-by-mailbox status details when `completed`.
- If bulk create used numeric `Params.password: 0`, each mailbox result includes its own generated `password` value.
POST
/api/v1/delete/domain/bulk
Bulk Delete Domains
Queues removal of up to 10 domains (root or sub-domain hostnames) owned by your account. Deletes run sequentially on the mail server; there is no artificial delay between items.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key` with `write` permission.
| Field |
Type |
Required |
Description |
| Domains |
string[] |
Yes |
Fully qualified hostnames to remove (1–10 per request). Duplicates are rejected. Same hostname rules as create domain (no scheme; valid dotted hostname). |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/delete/domain/bulk') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Domains": ["old.example.com", "legacy.example.com"]
}'
Success Response
{
"status": "success",
"task_id": 801
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create, bulk-delete, or manage domains, subdomains, or mailboxes via the API.
-
HTTP 422 - Validation error (empty array, too many domains, invalid hostname, or duplicate entries).
-
HTTP 429 - Too many requests (bulk domain delete is limited to one successfully enqueued call per 5 minutes per API key and IP).
Notes
- Cooldown: one successfully enqueued call per 5 minutes per API key and IP; HTTP 422 does not count. Mailbox bulk delete uses a separate counter of the same length.
- Poll `GET /api/v1/get/delete/domain/bulk?task_id=…` until `completed` or `failed`.
- When `completed`, `results` entries use `deleted`, `failed`, or `skipped` with optional `reason`; partial failures still yield task `completed`.
- Deleting a root domain removes descendant hostnames and their mailboxes in dependency order.
GET
/api/v1/get/delete/domain/bulk?task_id={task_id}
Get Bulk Delete Domains Task
Polls a bulk domain delete task created by `POST /api/v1/delete/domain/bulk`.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key` with `read` permission.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by Bulk Delete Domains. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/delete/domain/bulk') }}?task_id=801" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 801,
"total_email": 0,
"domain": "old.example.com",
"results": {
"old.example.com": {
"status": "deleted"
},
"legacy.example.com": {
"status": "failed",
"reason": "Mail server rejected domain removal."
},
"missing.example.com": {
"status": "skipped",
"reason": "Domain not found for this account."
}
},
"status": "completed",
"task_id": "801"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from `error_message` when the task fails.
Notes
- `status` may be `queued`, `running`, `completed`, or `failed`. Only `failed` (task-level) returns HTTP 422 with `reason` from the task.
- Per-domain outcomes (`deleted`, `failed`, or `skipped`) appear inside `results` while the overall task can still be `completed`.
POST
/api/v1/delete/mailbox/bulk
Bulk Delete Mailboxes
Queues removal of up to 100 mailbox addresses owned by your account. Deletes run sequentially; there is no artificial delay between items.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key` with `write` permission.
| Field |
Type |
Required |
Description |
| Mailboxes |
string[] |
Yes |
Full RFC email addresses (1–100 per request). Duplicates are rejected. |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/delete/mailbox/bulk') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"Mailboxes": ["a@example.com", "b@example.com"]
}'
Success Response
{
"status": "success",
"task_id": 802
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - An active pricing plan is required before you can create, bulk-delete, or manage domains, subdomains, or mailboxes via the API.
-
HTTP 422 - Validation error (empty array, too many addresses, invalid email, or duplicate entries).
-
HTTP 429 - Too many requests (bulk mailbox delete is limited to one successfully enqueued call per 5 minutes per API key and IP).
Notes
- Cooldown: one successfully enqueued call per 5 minutes per API key and IP; HTTP 422 does not count. Domain bulk delete uses a separate counter of the same length.
- Poll `GET /api/v1/get/delete/mailbox/bulk?task_id=…` until `completed` or `failed`.
- When `completed`, `results` entries use `deleted`, `failed`, or `skipped` with optional `reason`.
GET
/api/v1/get/delete/mailbox/bulk?task_id={task_id}
Get Bulk Delete Mailboxes Task
Polls a bulk mailbox delete task created by `POST /api/v1/delete/mailbox/bulk`.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key` with `read` permission.
| Field |
Type |
Required |
Description |
| task_id |
integer |
Yes |
Task identifier returned by Bulk Delete Mailboxes. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/get/delete/mailbox/bulk') }}?task_id=802" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"Task_id": 802,
"total_email": 2,
"domain": "example.com",
"results": {
"a@example.com": {
"status": "deleted"
},
"b@example.com": {
"status": "failed",
"reason": "Mail server rejected mailbox removal."
},
"gone@example.com": {
"status": "skipped",
"reason": "Mailbox not found for this account."
}
},
"status": "completed",
"task_id": "802"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Task ID was not found for this API key.
-
HTTP 422 - Task type does not match this endpoint.
-
HTTP 422 - Dynamic failure message from `error_message` when the task fails.
Notes
- `total_email` mirrors the request size (count of addresses submitted), not the number successfully deleted.
- Per-mailbox outcomes (`deleted`, `failed`, or `skipped`) appear inside `results` while the overall task can still be `completed`.
GET
/api/v1/list/mailbox?domain={domain}&include_credentials={0|1}
List Mailboxes
Returns all mailboxes for a specific owned domain, keyed by full email address.
Header auth required: `Authorization: Bearer <key>` or `X-Api-Key`.
| Field |
Type |
Required |
Description |
| domain |
string |
Yes |
Domain to list mailboxes for. |
| include_credentials |
boolean |
No |
Include `imap_credentials` in each mailbox result when true. |
Request Example
curl --fail-with-body --silent --show-error \
"{{ url('/api/v1/list/mailbox') }}?domain=example.com&include_credentials=1" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"domain": "example.com",
"results": {
"john@example.com": {
"provisioning_status": "active",
"mailbox_quota": "0MB",
"imap_credentials": {
"host": "imap.example.com",
"port": 993,
"encryption": "tls"
},
"created_at": "14-04-2026"
}
},
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Domain not found for this account.
Notes
- `imap_credentials` only when `include_credentials=true`.
- `mailbox_quota` is a string with an `MB` suffix; `0MB` means unlimited (non-zero may appear on older rows).
POST
/api/v1/mail/inbound
Inbound Email
Accepts inbound payloads for a tenant mailbox and queues processing. Optional fields include `from_name`, `cc`, `bcc`, `body_html`, `attachments`, and `headers`.
Requires API key with `inbound` permission.
| Field |
Type |
Required |
Description |
| message_id |
string |
Yes |
Upstream message identifier. Must be unique per mailbox. |
| from_email |
string |
Yes |
Sender email address. |
| to_email |
string |
Yes |
Recipient mailbox address owned by your tenant. |
| subject |
string |
Yes |
Email subject line. |
| received_at |
ISO datetime |
Yes |
Inbound receive timestamp. |
Request Example
curl --fail-with-body --silent --show-error \
-X POST "{{ url('/api/v1/mail/inbound') }}" \
-H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message_id": "<mail-message-id>",
"from_email": "sender@example.com",
"to_email": "user@example.com",
"subject": "Hello",
"body_text": "Body",
"received_at": "2026-04-28T10:00:00Z"
}'
Success Response
{
"status": "accepted",
"message_id": "<mail-message-id>"
}
Error Responses
-
HTTP 401 - API key is required or invalid.
-
HTTP 403 - API key lacks inbound permission.
-
HTTP 409 - Duplicate message_id.
-
HTTP 422 - Unknown mailbox or invalid payload.
Notes
- Inbound processing is asynchronous.
GET
/api/v1/emails
List Emails
Returns tenant-scoped inbox emails with cursor pagination.
Requires API key with `read` permission.
| Field |
Type |
Required |
Description |
| mailbox_id |
integer |
No |
Filter by mailbox. |
| unread |
boolean |
No |
Return unread only. |
| since |
ISO datetime |
No |
Filter received_at lower bound. |
| before |
ISO datetime |
No |
Filter received_at upper bound. |
Request Example
curl "{{ url('/api/v1/emails?unread=1') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"data": [],
"meta": {
"next_cursor": null,
"prev_cursor": null
}
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks read permission.
Notes
- Query is always tenant-isolated.
GET
/api/v1/emails/{email}
Get Email
Returns one tenant-scoped email with attachment metadata.
Requires API key with `read` permission.
| Field |
Type |
Required |
Description |
| email |
integer |
Yes |
Email record ID (route parameter `{email}`). |
Request Example
curl "{{ url('/api/v1/emails/123') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"data": {
"id": 123
}
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Email not found in your tenant scope.
Notes
- Soft-deleted records are not returned from this endpoint.
GET
/api/v1/emails/sync?since=ISO_TIMESTAMP
Sync Emails
Incremental polling endpoint for new, updated, and deleted emails.
Requires API key with `read` permission.
| Field |
Type |
Required |
Description |
| since |
ISO datetime |
Yes |
Sync lower bound using updated markers. |
Request Example
curl "{{ url('/api/v1/emails/sync?since=2026-04-28T00:00:00Z') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"data": [],
"meta": {
"next_cursor": null,
"prev_cursor": null
}
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 422 - The since field is required and must be a valid date.
Notes
- Deleted emails are returned with deletion markers to support tombstones.
POST
/api/v1/emails/{email}/read
Mark Email Read
Marks an email as read and emits an email.read webhook event.
Requires API key with `write` permission.
| Field |
Type |
Required |
Description |
| email |
integer |
Yes |
Email record ID (route parameter `{email}`). |
Request Example
curl -X POST "{{ url('/api/v1/emails/123/read') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks write permission.
-
HTTP 404 - Email not found in your tenant scope.
Notes
- This endpoint is idempotent.
DELETE
/api/v1/emails/{email}
Delete Email
Soft-deletes an email and emits an email.deleted webhook event.
Requires API key with `write` permission.
| Field |
Type |
Required |
Description |
| email |
integer |
Yes |
Email record ID (route parameter `{email}`). |
Request Example
curl -X DELETE "{{ url('/api/v1/emails/123') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks write permission.
-
HTTP 404 - Email not found in your tenant scope.
Notes
- Record is soft deleted, not permanently removed.
GET
/api/v1/emails/{email}/attachments/{attachment}
Attachment URL
Returns a temporary signed attachment download URL.
Requires API key with `read` permission.
| Field |
Type |
Required |
Description |
| email |
integer |
Yes |
Email record ID (route parameter `{email}`). |
| attachment |
integer |
Yes |
Attachment record ID (route parameter `{attachment}`). |
Request Example
curl "{{ url('/api/v1/emails/123/attachments/456') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"data": {
"url": "https://signed-url",
"expires_at": "2026-04-28T10:10:00Z"
}
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 404 - Email or attachment not found in your tenant scope.
Notes
- URL expires shortly after issuance.
POST
/api/v1/webhooks
Register Webhook
Creates a tenant-scoped webhook target for email events.
Requires API key with `webhook` permission.
| Field |
Type |
Required |
Description |
| url |
url |
Yes |
Destination webhook URL. |
| events |
array |
Yes |
Allowed values: email.received, email.read, email.deleted. |
Request Example
curl -X POST "{{ url('/api/v1/webhooks') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" -H "Content-Type: application/json" -d '{"url":"https://example.com/hook","events":["email.received"]}'
Success Response
{
"data": {
"id": 1
}
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks webhook permission.
-
HTTP 422 - Invalid URL or event selection.
Notes
- Deliveries are queued and signed using the webhook secret and timestamp headers.
GET
/api/v1/webhooks
List Webhooks
Returns tenant-scoped webhook endpoints.
Requires API key with `webhook` permission.
| Field |
Type |
Required |
Description |
Request Example
curl "{{ url('/api/v1/webhooks') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"data": [
{
"id": 1,
"url": "https://example.com/hook",
"events": [
"email.received"
],
"is_active": true
}
]
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks webhook permission.
Notes
- Each row includes `last_triggered_at` when the webhook has fired.
PATCH
/api/v1/webhooks/{webhook}
Update Webhook
Updates URL, events, or active state.
Requires API key with `webhook` permission.
| Field |
Type |
Required |
Description |
| webhook |
integer |
Yes |
Webhook ID (route parameter `{webhook}`). |
| url |
url |
No |
Destination webhook URL. |
| events |
array |
No |
Allowed values: email.received, email.read, email.deleted. |
| is_active |
boolean |
No |
Whether webhook should receive deliveries. |
Request Example
curl -X PATCH "{{ url('/api/v1/webhooks/1') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY" -H "Content-Type: application/json" -d '{"is_active":false}'
Success Response
{
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks webhook permission.
-
HTTP 404 - Webhook not found in your tenant scope.
-
HTTP 422 - Invalid URL or event selection.
Notes
- Include at least one of `url`, `events`, or `is_active`.
DELETE
/api/v1/webhooks/{webhook}
Delete Webhook
Deletes a tenant webhook endpoint.
Requires API key with `webhook` permission.
| Field |
Type |
Required |
Description |
| webhook |
integer |
Yes |
Webhook ID (route parameter `{webhook}`). |
Request Example
curl -X DELETE "{{ url('/api/v1/webhooks/1') }}" -H "Authorization: Bearer YOUR_32_CHARACTER_API_KEY"
Success Response
{
"status": "success"
}
Error Responses
-
HTTP 401 - Invalid API key.
-
HTTP 403 - API key lacks webhook permission.
-
HTTP 404 - Webhook not found in your tenant scope.
Notes
- Permanent delete; queued delivery rows for this webhook are removed.