Skip to content

Mailexam REST API

The Mailexam REST API lets you manage projects, inboxes, and messages programmatically — ideal for automated tests, QA, and CI/CD. A typical flow: your app sends mail via SMTP, and a test uses the API to confirm delivery and assert on subject, body, or attachments.

The full OpenAPI spec is available in Swagger UI.

Base URL

Parameter Value
Base URL https://mailexam.io/api/v1
Format JSON
Methods GET, POST, PUT, DELETE

Regional domains

Region API
Russia mailexam.ru
China mailexam.cn
International mailexam.io

Paths and request format are the same; only the host differs.

Authentication

Every request requires an API token from the Mailexam dashboard.

Pass the token as the query parameter token:

curl -s "https://mailexam.io/api/v1/project?token=YOUR_API_TOKEN"

Secrets in CI/CD

Do not commit the token to your repository. Store it as MAILEXAM_API_TOKEN in GitLab CI, GitHub Actions, or your pipeline secrets.

Status Meaning
200 / 201 Success
403 Insufficient permissions or invalid token
404 Object not found for the given UUID

Typical CI/CD flow

  1. Send mail from your app via SMTP (see integration examples).
  2. Wait for delivery — usually 1–5 seconds.
  3. Fetch projects → inboxes → messages through the API.
  4. Find the message by subject or recipient and assert on content.
sequenceDiagram
    participant App as Application
    participant SMTP as Mailexam SMTP
    participant API as REST API
    participant Test as Automated test

    App->>SMTP: sendMail()
    Note over SMTP: Message in sandbox
    Test->>API: GET /project
    Test->>API: GET /inbox?project=...
    Test->>API: GET /email?inbox=...
    Test->>Test: assert subject, body

Endpoint reference

Projects (/project)

Method Path Description
GET /project List projects
POST /project/store Create project
GET /project/{uuid} Project details
PUT /project/{uuid}/update Update project
DELETE /project/{uuid}/delete Delete project

Request body for create/update:

{
  "name": "My project",
  "description": null
}

Sample response (ProjectResponse):

{
  "uuid": "536a47df-5aad-44d0-8163-a39bb55abe0b",
  "name": "My project",
  "description": null,
  "user": {
    "uuid": "984f1f70-21e1-4244-bbb7-734ac151dcdc",
    "name": "Mailexam Support",
    "email": "support@mailexam.io"
  }
}

Inboxes (/inbox)

Method Path Description
GET /inbox?project={uuid} List inboxes in a project
POST /inbox/store Create inbox
GET /inbox/{uuid} Inbox details
PUT /inbox/{uuid}/default Set as default inbox
PUT /inbox/{uuid}/update Update inbox
DELETE /inbox/{uuid}/delete Delete inbox

Create request body (InboxRequest):

{
  "project_uuid": "536a47df-5aad-44d0-8163-a39bb55abe0b",
  "name": "All messages",
  "has_autorelay": false,
  "relay_host": null,
  "relay_port": null,
  "relay_pass": null
}

Messages (/email)

Method Path Description
GET /email?inbox={uuid} List messages in an inbox
GET /email/{uuid} Full message (body, headers)
GET /email/{uuid}/attachment?cid={id} Attachment
PUT /email/{uuid}/unread Mark unread/read
PUT /email/{uuid}/like Like toggle
PUT /email/{uuid}/dislike Dislike toggle
PUT /email/{uuid}/relay Relay to SMTP server
DELETE /email/{uuid}/delete Delete message

Short message format (EmailShort):

{
  "uuid": "e2f9a506-d766-4935-be11-c413384de020",
  "from": "\"Mailexam\" <support@mailexam.io>",
  "to": "Mailexam Support <support@mailexam.io>",
  "cc": null,
  "subject": "Mailexam connection parameters",
  "human_size": "113 Bytes",
  "date": "2025-03-01T08:58:49.000000Z",
  "attachments": true,
  "unread": false,
  "like": true,
  "dislike": false
}

Full format (EmailDetailed) also includes body (html, text, raw, attachments), headers, and relay.

Example: verify a message with curl

Environment variables:

export MAILEXAM_API_TOKEN="your_token"
export MAILEXAM_API_BASE="https://mailexam.io/api/v1"
curl -s "${MAILEXAM_API_BASE}/project?token=${MAILEXAM_API_TOKEN}" | jq .
PROJECT_UUID="536a47df-5aad-44d0-8163-a39bb55abe0b"

curl -s "${MAILEXAM_API_BASE}/inbox?project=${PROJECT_UUID}&token=${MAILEXAM_API_TOKEN}" | jq .
INBOX_UUID="deab7974-a252-4412-9169-b965116b63cf"

curl -s "${MAILEXAM_API_BASE}/email?inbox=${INBOX_UUID}&token=${MAILEXAM_API_TOKEN}" | jq .
EMAIL_UUID="e2f9a506-d766-4935-be11-c413384de020"

curl -s "${MAILEXAM_API_BASE}/email/${EMAIL_UUID}?token=${MAILEXAM_API_TOKEN}" | jq .

Example: Python automated test

After SMTP send, find a message by subject and assert on body text.

import os
import time
import urllib.parse
import urllib.request
import json

API_BASE = os.environ.get("MAILEXAM_API_BASE", "https://mailexam.io/api/v1")
TOKEN = os.environ["MAILEXAM_API_TOKEN"]
PROJECT_UUID = os.environ["MAILEXAM_PROJECT_UUID"]
EXPECTED_SUBJECT = "CI check"


def api_get(path, params=None):
    params = dict(params or {})
    params["token"] = TOKEN
    url = f"{API_BASE}{path}?{urllib.parse.urlencode(params)}"
    with urllib.request.urlopen(url) as resp:
        return json.loads(resp.read().decode())


def wait_for_email(subject, timeout=30, interval=2):
    inboxes = api_get("/inbox", {"project": PROJECT_UUID})
    inbox_uuid = inboxes[0]["uuid"]
    deadline = time.time() + timeout

    while time.time() < deadline:
        emails = api_get("/email", {"inbox": inbox_uuid})
        for item in emails:
            if item["subject"] == subject:
                return api_get(f"/email/{item['uuid']}")
        time.sleep(interval)

    raise TimeoutError(f"Message with subject «{subject}» not found in {timeout}s")


if __name__ == "__main__":
    # ... send mail via your app's SMTP here ...
    time.sleep(3)

    email = wait_for_email(EXPECTED_SUBJECT)
    body_text = (email.get("body") or {}).get("text") or ""
    assert "Hello" in body_text, "Body mismatch"
    print("OK")

Uses Python 3 stdlib only. Use requests or httpx if already in your project.

Example: Node.js automated test

const TOKEN = process.env.MAILEXAM_API_TOKEN;
const API_BASE = process.env.MAILEXAM_API_BASE || 'https://mailexam.io/api/v1';
const PROJECT_UUID = process.env.MAILEXAM_PROJECT_UUID;
const EXPECTED_SUBJECT = 'CI check';

async function apiGet(path, params = {}) {
  const query = new URLSearchParams({ ...params, token: TOKEN });
  const res = await fetch(`${API_BASE}${path}?${query}`);
  if (!res.ok) throw new Error(`${res.status} ${path}`);
  return res.json();
}

async function waitForEmail(subject, timeoutMs = 30000, intervalMs = 2000) {
  const inboxes = await apiGet('/inbox', { project: PROJECT_UUID });
  const inboxUuid = inboxes[0].uuid;
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const emails = await apiGet('/email', { inbox: inboxUuid });
    const match = emails.find((item) => item.subject === subject);
    if (match) return apiGet(`/email/${match.uuid}`);
    await new Promise((r) => setTimeout(r, intervalMs));
  }

  throw new Error(`Message «${subject}» not found in ${timeoutMs / 1000}s`);
}

(async () => {
  // ... SMTP send ...
  await new Promise((r) => setTimeout(r, 3000));

  const email = await waitForEmail(EXPECTED_SUBJECT);
  const text = email.body?.text || '';
  if (!text.includes('Hello')) throw new Error('Body mismatch');
  console.log('OK');
})().catch((err) => {
  console.error(err);
  process.exit(1);
});

Requires Node.js 18+ (built-in fetch).

GitLab CI

integration_test:
  stage: test
  variables:
    MAILEXAM_API_BASE: "https://mailexam.io/api/v1"
    MAILEXAM_PROJECT_UUID: "536a47df-5aad-44d0-8163-a39bb55abe0b"
  script:
    - python send_and_verify.py
  # MAILEXAM_API_TOKEN, MAILEXAM_LOGIN, MAILEXAM_PASSWORD — in CI/CD Variables

SMTP credentials (MAILEXAM_LOGIN, MAILEXAM_PASSWORD) and the API token are different values: the former for sending, the latter for reading via REST.

Mailtrap API comparison

When migrating from Mailtrap, replace Sandbox API calls:

Task Mailtrap Mailexam
List messages Sandbox API GET /email?inbox={uuid}
Single message Sandbox API GET /email/{uuid}
Attachment Sandbox API GET /email/{uuid}/attachment?cid={id}
Manage sandbox Sandboxes API GET /project, /inbox

Troubleshooting

403 Forbidden

  • Ensure the token is complete and passed as ?token=....
  • The token belongs to your account — verify the project is under the same user.

Empty message list

  • The message may have landed in a different project or inbox — match the SMTP login to the correct project.
  • Increase the pause after SMTP send (2–5 s) or use polling with a timeout.

Message visible in the dashboard but not in tests

  • Filter by exact subject or check the latest messages by date in the API response.
  • For attachments, fetch EmailDetailed first, then download by cid.

See also