Inbox management

Register an inbox address to start receiving email at that address. You can list and remove inboxes via the API.

GET /v1/inboxes

Returns all inboxes registered to your account.

curl
curl "https://api2.freecustom.email/v1/inboxes" \
  -H "Authorization: Bearer fce_your_api_key"

Responses

200Success

json
{
  "success": true,
  "data": {
    "inboxes": ["test@freecustom.email", "staging@acme.com"],
    "count": 2
  }
}

200Empty

json
{
  "success": true,
  "data": { "inboxes": [], "count": 0 }
}

401Missing or invalid API key

json
{
  "success": false,
  "error": "unauthorized",
  "message": "Missing or invalid API key. Pass your key in the Authorization header: Bearer fce_..."
}

POST /v1/inboxes

Request body:

FieldTypeRequiredDescription
inboxstringYesFull email address (e.g. test@freecustom.email)
isTestingbooleanNoFlag this inbox for testing purposes to enable zero-latency event timelines in the Auth Flow Debugger.
curl
curl -X POST https://api2.freecustom.email/v1/inboxes \
  -H "Authorization: Bearer fce_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"inbox":"mytest@ditapi.info", "isTesting": true}'

Responses

201Inbox registered

json
{
  "success": true,
  "data": {
    "inbox": "mytest@ditapi.info",
    "registered_at": "2026-03-04T10:00:00.000Z"
  }
}

400Missing inbox

json
{
  "success": false,
  "error": "missing_field",
  "message": "inbox is required."
}

400Invalid email format

json
{
  "success": false,
  "error": "invalid_inbox",
  "message": "\"not-an-email\" is not a valid email address."
}

403Custom domain requires upgrade

json
{
  "success": false,
  "error": "plan_restriction",
  "message": "Custom domain inboxes require the Startup plan or higher.",
  "upgrade_url": "https://freecustom.email/api/pricing"
}

409Already registered

json
{
  "success": false,
  "error": "already_registered",
  "message": "mytest@ditapi.info is already registered to this account."
}

POST /v1/inboxes/{inbox}/tests

Marks the start of a testing boundary for this inbox. Useful in test suites (Playwright, Cypress, Selenium) to group timeline events together under a single test run ID. Logs a test_started event in the Timeline.

FieldTypeRequiredDescription
test_idstringNoOptional custom test ID. If omitted, one will be generated automatically.
curl
curl -X POST https://api2.freecustom.email/v1/inboxes/mytest@ditapi.info/tests \
  -H "Authorization: Bearer fce_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"test_id":"e2e-signup-1"}'

Responses

200Test started

json
{
  "success": true,
  "data": {
    "test_id": "e2e-signup-1",
    "inbox": "mytest@ditapi.info",
    "started_at": "2026-03-04T10:05:00.000Z"
  }
}

GET /v1/inboxes/{inbox}/timeline

Returns the timeline history for a specific inbox. This shows the test run history and event sequence for debugging failed tests. If the inbox was registered with isTesting: true, zero-latency early timeline events like smtp_rcpt_received will be tracked. Cost: 2 requests.

FieldTypeRequiredDescription
test_idstringNoOptional test ID to filter the timeline events by a specific test run.
curl
curl "https://api2.freecustom.email/v1/inboxes/mytest@ditapi.info/timeline?test_id=e2e-signup-1" \
  -H "Authorization: Bearer fce_your_api_key"

Responses

200Success

json
{
  "success": true,
  "data": {
    "inbox": "mytest@ditapi.info",
    "events": [
      { "id": "evt_1", "type": "test_started", "timestamp": 1714152000000, "test_run_id": "e2e-signup-1" },
      { "id": "evt_2", "type": "smtp_rcpt_received", "timestamp": 1714152000500, "latency_ms": 500, "test_run_id": "e2e-signup-1" },
      { "id": "evt_3", "type": "email_received", "timestamp": 1714152001000, "latency_ms": 1000, "test_run_id": "e2e-signup-1", "metadata": { "subject": "Verify your email" } },
      { "id": "evt_4", "type": "otp_extracted", "timestamp": 1714152001200, "latency_ms": 200, "test_run_id": "e2e-signup-1", "metadata": { "otp": "123456", "score": 0.99 } }
    ],
    "duration_hours": 24,
    "event_count": 4
  }
}

GET /v1/inboxes/{inbox}/insights

Returns automated failure insights for a specific inbox. This analyzes the inbox activity and detects common issues with test flows. Cost: 3 requests.

curl
curl "https://api2.freecustom.email/v1/inboxes/mytest@ditapi.info/insights" \
  -H "Authorization: Bearer fce_your_api_key"

Responses

200Success

json
{
  "success": true,
  "data": {
    "inbox": "mytest@ditapi.info",
    "insights": [
      { "type": "slow_delivery", "message": "Email took >3s" },
      { "type": "multiple_detected", "message": "Multiple emails detected" }
    ],
    "analyzed_at": "2026-03-04T10:05:00.000Z"
  }
}

Inbox Generator — Bulk generation

The Inbox Generator lets you create hundreds of disposable inboxes in a single API call, each pre-wired with a long-lived public OTP polling URL. It is designed for high-volume bot workflows, multi-account testing, CI/CD pipelines, and any automation that needs to create inboxes at scale without exposing your main API key.

Gen API key

The generator uses a scoped Gen API key (fcegen_…) that is separate from your main developer key. It can only call POST /v1/inboxes/generate and GET /v1/otp/public — safe to embed in scripts, CI jobs, or Playwright tests without exposing your full-access key. Create and manage it from the Dashboard → Inbox Generator tab.

POST /v1/inboxes/generate

Requires Developer plan or above. Authenticate with your fcegen_ Gen API key.

FieldTypeDefaultDescription
countinteger1Number of inboxes to generate.
username_stylestringrandom_charsrandom_chars, digits_only, letters_only, name, noun_digits, firstname.surname
domain_modestringanyany, free_only, pro_only, custom_only, specific
output_formatstring{email}----{otp_url}Template for each output line. Variables: {email}, {username}, {domain}, {otp_url}, {token}
sinceintegerUnix ms timestamp embedded as ?since= in every OTP URL. Prevents stale codes from previous sessions.
parseCodebooleantrueEmbeds ?parseCode=true in OTP URLs, enabling on-demand extraction at poll time. Required for API inboxes (see below).
curl
curl -X POST https://api2.freecustom.email/v1/inboxes/generate \
  -H "Authorization: Bearer fcegen_your_gen_key" \
  -H "Content-Type: application/json" \
  -d '{
    "count": 5,
    "username_style": "firstname.surname",
    "domain_mode": "any",
    "since": 1716900000000,
    "parseCode": true
  }'

Response

200Generated

json
{
  "success": true,
  "count": 5,
  "daily_used": 5,
  "daily_limit": 500,
  "daily_remaining": 495,
  "inboxes": [
    {
      "email": "jade.quasar21@addmy.space",
      "username": "jade.quasar21",
      "domain": "addmy.space",
      "token": "fceotp_24f1add500d18150a02c62e40b395828226a79ab",
      "otp_url": "https://api2.freecustom.email/v1/otp/public?token=fceotp_…&since=1716900000000&parseCode=true",
      "formatted": "jade.quasar21@addmy.space----https://api2.freecustom.email/v1/otp/public?token=fceotp_…"
    }
  ]
}

GET /v1/otp/public — keyless OTP polling

Each generated inbox includes an otp_url that you can poll directly — no API key needed. Tokens are valid for 365 days. Use ?since= to avoid getting codes from a previous session, and ?parseCode=true to trigger on-demand extraction (required for API inboxes).

curl
# Poll for OTP — no API key required
curl "https://api2.freecustom.email/v1/otp/public?token=fceotp_24f1add500d18150a02c62e40b395828226a79ab&since=1716900000000&parseCode=true"

200OTP found

json
{
  "success": true,
  "otp": "340108",
  "code": "340108",
  "inbox": "jade.quasar21@addmy.space",
  "score": 0.8,
  "email_id": "zUllTeLcT4",
  "from": "\"Apple\" <appleid@id.apple.com>",
  "subject": "Verify your Apple Account email address.",
  "timestamp": 1716900060000,
  "verification_link": null
}

200No OTP yet

json
{
  "success": true,
  "otp": null,
  "code": null,
  "inbox": "jade.quasar21@addmy.space",
  "message": "No OTP found in messages received after 2026-05-29T18:00:00.000Z."
}

Fast-save mode & on-demand extraction

API-generated inboxes (isApiInbox) skip OTP extraction at SMTP delivery time to maximise throughput. The email is saved to Redis immediately with otp: null. When you poll with ?parseCode=true, the API fetches the stored subject and body, runs the multilingual regex extractor (<1 ms, supports 20+ languages including Chinese, Japanese, Korean, Arabic), and returns the live OTP — all in the same HTTP response. No re-parsing, no extra latency.

If you do not pass ?parseCode=true, the endpoint returns otp: null for API inboxes. The Inbox Generator UI and the generated OTP URLs both include parseCode=true by default.

Full bot workflow example

python
import time, requests

GEN_KEY  = "fcegen_your_gen_key"
API_BASE = "https://api2.freecustom.email/v1"

# 1. Generate an inbox
since = int(time.time() * 1000)
r = requests.post(f"{API_BASE}/inboxes/generate",
    headers={"Authorization": f"Bearer {GEN_KEY}"},
    json={"count": 1, "since": since, "parseCode": True})
inbox = r.json()["inboxes"][0]
email, otp_url = inbox["email"], inbox["otp_url"]

# 2. Use email on the target site (e.g. Apple ID sign-up)
# ... trigger_signup(email) ...

# 3. Poll until OTP arrives (up to 60 s)
for _ in range(20):
    time.sleep(3)
    resp = requests.get(otp_url).json()
    if resp.get("otp"):
        print("OTP:", resp["otp"])
        break

DELETE /v1/inboxes/{inbox}

Unregisters the inbox. Path inbox must be URL-encoded (e.g. test%40freecustom.email).

curl
curl -X DELETE "https://api2.freecustom.email/v1/inboxes/mytest%40ditapi.info" \
  -H "Authorization: Bearer fce_your_api_key"

Responses

200Unregistered

json
{
  "success": true,
  "message": "mytest@ditapi.info has been unregistered."
}

404Not registered

json
{
  "success": false,
  "error": "not_found",
  "message": "mytest@ditapi.info is not registered on this account."
}