onyx mail
API reference · v1

Temporary email, as an API.

Create disposable mailboxes, poll an inbox, read full HTML messages, and pull a premium Gmail inbox from the shared pool. No signup, no API key — the mailbox id is the credential.

01Agent prompt — copy & paste

Tempel blok di bawah ini apa adanya ke AI agent (Claude, GPT, Cursor, n8n, LangChain, dll). Prompt-nya sudah self-contained: agent langsung tahu base URL, cara auth, urutan langkah, dan bentuk response-nya.

System prompt for AI agents
paste into your agent's system / developer message
# Tool: disposable email (sixty9 mail)

You have access to a disposable email API at https://mail.sixty9.tech. Use it whenever you need to
receive a verification email, OTP link, or signup confirmation without a real inbox.

## Auth
- No API key. A mailbox id IS the credential.
- Every read call takes:  Authorization: Bearer <token>
- The prefix is picked automatically from the domain:
    bx_<boxId>                      -> 1timetech domains (oletters.com, rommiui.com, ...)
    gm_<base64url({a,sid})>          -> guerrillamailblock.com
    gr_<base64url(address)>          -> grabmail domains (grabmail.io, mixozia.com, linqmail.com)
    zv_<base64url(address)>          -> zenvex domains (vyno.me, znvx.me, souss.dev, *.edu.pl)
- Easiest: POST /token {"address": "..."} and use the token it returns.

## Standard workflow
0. GET /status     -> providers + domains with verified true/false + receive mode
   GET /verify?address=x@d -> is that mailbox reachable right now, how many messages
1. GET /health     -> confirm service + provider health, list valid domains
2. POST /accounts {"address":"bot1@oletters.com"} -> {"id":"<boxId>","address":"...","token":"..."}
3. POST /token    {"address":"bot1@oletters.com"} -> {"token":"bx_<boxId>","id":"<boxId>"}
4. wait ~5-10s, then poll GET /messages with the Bearer token every 5s (max 24 tries,
   back off to 10s after 6 empty polls)
5. GET /messages/<messageId>                    -> full body: .text and .html[0]
6. Extract the OTP/link from .text or .html[0], then finish your task.

## Rules
- Creating the same address twice returns the SAME id (idempotent) - safe to retry.
- Never invent domains: take them from GET /health -> .domains, and prefer the ones
  /status marks verified=true.
- POOL DOMAINS (gmail.com, googlemail.com, +gmail.com, +googlemail.com): the typed
  address is IGNORED. The call returns a real pooled Gmail inbox - always use the
  address in the response, not the one you asked for.
- Premium: POST /gmail (empty body) returns a random REAL gmail inbox from a shared
  pool. Its messages are other people's mail - use it only for throwaway testing,
  never for anything private, and never send mail from it.
- If a call returns 502, retry once after 3s. If it still fails, report the failure;
  do not fabricate an email body.
- Poll politely: one request every 5s per mailbox. Do not run parallel pollers.

## Endpoints (all same origin)
GET  /health                     -> {ok, domains[], premiumDomains[]}
GET  /status                     -> {counts, verifiedDomains[], untestedDomains[],
                                     poolDomains[], providers[], domains[]}
GET  /verify?address=            -> {ok, provider, messages, verified}
GET  /llms.txt                   -> compact plain-text briefing for agents
GET  /domains                    -> [{domain,isActive,isPrivate,isPremium}]
POST /accounts  {address}        -> {id, address}
POST /token     {address}        -> {token, id, address}
GET  /messages      (Bearer)     -> {hydra:member:[{id,from,to,subject,intro,createdAt,hasAttachments}], totalItems}
GET  /messages/{id} (Bearer)     -> {..., text, html[], attachments[]}
GET  /sources/{id}  (Bearer)     -> raw html
POST /gmail                      -> {id, address, token}

## Example (curl)
ID=$(curl -s -X POST https://mail.sixty9.tech/accounts -H 'content-type: application/json' \
  -d '{"address":"bot1@oletters.com"}' | jq -r .id)
curl -s https://mail.sixty9.tech/messages -H "Authorization: Bearer bx_$ID" | jq '.hydra:member[0]'
Tip: kalau agent lo support tool-calling, pakai JSON schema di section 06 — jauh lebih akurat daripada parsing curl.

02Quick start

# 1. cek service + domain yang valid
curl -s https://mail.sixty9.tech/health

# 2. bikin mailbox
curl -s -X POST https://mail.sixty9.tech/accounts \
  -H 'content-type: application/json' \
  -d '{"address":"bot1@oletters.com"}'
# -> {"id":"6c3da3c9...","address":"bot1@oletters.com"}

# 3. ambil token
curl -s -X POST https://mail.sixty9.tech/token \
  -H 'content-type: application/json' \
  -d '{"address":"bot1@oletters.com"}'
# -> {"token":"bx_6c3da3c9...","id":"6c3da3c9..."}

# 4. baca inbox
curl -s https://mail.sixty9.tech/messages \
  -H 'Authorization: Bearer bx_6c3da3c9...'

# 5. baca satu pesan (full html)
curl -s https://mail.sixty9.tech/messages/<messageId> \
  -H 'Authorization: Bearer bx_6c3da3c9...'

03Auth model

base url
https://mail.sixty9.tech
credential
mailbox id — tidak ada API key global selain ini
header
Authorization: Bearer <token>
token · 1timetech
bx_<boxId> — domain HD (oletters, rommiui, …)
token · guerrillamail
gm_<base64url({a,sid})> — domain guerrillamailblock.com
token · grabmail
gr_<base64url(address)> — grabmail.io, mixozia.com, linqmail.com
token · zenvex
zv_<base64url(address)> — vyno.me, znvx.me, souss.dev, 3× edu.pl
token · pool
bx_<boxId> — gmail.com / googlemail.com / +varian: pilih domain ini = klaim inbox Gmail asli dari shared pool (alamat yang diketik tidak dipakai)
routing
provider ditentukan otomatis dari domain saat create / token
idempotent
POST /accounts dengan address sama → id sama
Stateless: token tidak bisa di-revoke dan tidak expire. Siapa pun yang tahu bx_<id> bisa baca inbox itu. Jangan pakai untuk data sensitif permanen.

04Endpoints

RouteWhat it does
GET/healthStatus + health per provider + daftar domain live + premium domains.
GET/domainsDomain list format mail.tm + field provider (hd / gm / gr / zv / pool).
POST/accountsBody {"address":"user@domain"} → buat/ambil mailbox. Return {id,address}.
POST/tokenBody {"address":"user@domain"} → {token,id,address}.
GET/messagesBearer token → list pesan (hydra:member + member).
GET/messages/{id}Bearer token → satu pesan penuh: text + html[].
GET/sources/{id}Bearer token → HTML mentah (text/html), buat dirender langsung.
POST/gmailTanpa body → inbox Gmail asli dari shared pool {id,address,token}.
DELETE/messages/{id}Return 204. Upstream tidak punya delete — ini no-op biar kompatibel.
Catatan: penghapusan inbox di web app bersifat lokal (menghapus dari daftar lo). Upstream tidak menyediakan endpoint hapus mailbox — alamat tetap hidup di server mereka sampai expired sendiri.

05Response shapes

// GET /health
{ "ok": true, "gateway": "timetech-mail", "upstream": "https://mail-server.1timetech.com/api",
  "domains": ["gmail10p.com","oletters.com","oemails.com","oegmail.com","suiemail.com","voewo.com","yanemail.com","rommiui.com"],
  "premiumDomains": ["gmail.com","googlemail.com","tempmail.edu.pl"] }

// GET /messages
{ "hydra:member": [{
    "id": "1bb5852b-ccb7-44d0-9f71-91ddf6e0085d",
    "accountId": "4bfe82a7a95d5bd28d0367c4a317c5a5",
    "from": { "address": "shein@edm.ph.sheinemail.com", "name": "" },
    "to":   [{ "address": "k.i.lia.n.oko.ndur.u@gmail.com", "name": "" }],
    "subject": "✨ Best Sellers: The Styles to Know",
    "intro": "SHEIN Discover the gear everyone is wearing right…",
    "seen": false, "isDeleted": false,
    "hasAttachments": false, "size": 812,
    "createdAt": "2026-11-02T13:50:18.322Z", "updatedAt": "2026-11-02T13:50:18.322Z"
  }],
  "hydra:totalItems": 1, "member": [ … ], "totalItems": 1 }

// GET /messages/{id}  (extra fields)
{ "…": "same as above", "text": "plain text preview",
  "html": ["<!doctype html><html>…full email html…"],
  "attachments": [] }

06LLM tool schema (OpenAI / Anthropic style)

Paste into the tools array. Works with GPT function calling, Claude tools, LangChain StructuredTool, n8n HTTP tool, dan sejenisnya.

[
  { "type": "function", "function": {
      "name": "temp_mail_create",
      "description": "Create or fetch a disposable mailbox. Idempotent by address.",
      "parameters": { "type": "object", "required": ["address"], "properties": {
        "address": { "type": "string", "description": "user@domain, domain must come from temp_mail_domains" } } } } },

  { "type": "function", "function": {
      "name": "temp_mail_messages",
      "description": "List messages in a mailbox.",
      "parameters": { "type": "object", "required": ["mailbox_id"], "properties": {
        "mailbox_id": { "type": "string", "description": "id returned by temp_mail_create" } } } } },

  { "type": "function", "function": {
      "name": "temp_mail_read",
      "description": "Read one message including full HTML body.",
      "parameters": { "type": "object", "required": ["mailbox_id","message_id"], "properties": {
        "mailbox_id": { "type": "string" }, "message_id": { "type": "string" } } } } },

  { "type": "function", "function": {
      "name": "temp_mail_domains",
      "description": "List available domains (free + premium).",
      "parameters": { "type": "object", "properties": {} } } },

  { "type": "function", "function": {
      "name": "temp_mail_gmail_pool",
      "description": "Claim a random real Gmail inbox from the shared pool. Testing only.",
      "parameters": { "type": "object", "properties": {} } } }
]
Endpoint mapping: temp_mail_create → POST /accounts · temp_mail_messages → GET /messages · temp_mail_read → GET /messages/{id} · temp_mail_domains → GET /health · temp_mail_gmail_pool → POST /gmail.

07Limits & notes

Rate & polling

Poll 1 inbox per 5 detik, jangan paralel. Worker-nya di edge, tapi upstream-nya server pihak ketiga — kalau dihajar, semua orang kena limit.

Deliverability

Alamat langsung aktif begitu dibuat. Email dari layanan besar (Google, SHEIN, dll) biasanya masuk dalam 5–30 detik.

Retensi

Mailbox di upstream bisa expired kapan saja. Simpan data yang penting di sisi lo, jangan andalkan inbox ini sebagai storage.

grabmail

Keyless, retensi 5 hari. Kuota 60 req/menit per IP — Worker egress Cloudflare dipakai bersama, jadi sesekali bisa 429. Tiap mailbox juga punya alias (domain privat) yang aman dipakai di form.

zenvex

Domain .edu.pl + .dev. Akses lewat sesi browser (cookie Turnstile) yang di-refresh otomatis tiap 6 jam. Kalau health zv:false, minter belum jalan.

Privasi

Pool Gmail berisi inbox nyata milik orang lain. Pakai hanya untuk testing, jangan untuk apa pun yang sensitif atau ilegal.

08Domain status & e2e

Nggak semua domain sama. Ada yang udah kebukti nerima email end-to-end lewat gateway ini, ada yang belum pernah dites, dan ada yang bukan alamat nyata (pool). Cek /status sebelum milih domain.

ProviderReceiveVerifiedCatatan
hd · 1timetechaddress✅ e2eDomain native app. Kebukti: oletters.com nerima email dari luar.
zv · zenvexaddress✅ e2eTermasuk 4 domain .edu.pl. Body diambil dari /api/inbox/{id}, list cuma metadata.
gm · guerrillamailaddress✅ e2eguerrillamailblock.com — nerima Cloudflare + welcome mail.
gr · grabmailaddress✅ e2eKetiga domain kebukti. 5 hari retensi, ada alias privat. 60 req/menit per IP → bisa 429.
pool · gmail poolpool✅ e2eAlamat yang diketik tidak dipakai; yang balik inbox Gmail asli dari pool.

Cek cepat

GET /status                       # provider + domain + verified/untested
GET /verify?address=x@domain      # mailbox ini hidup sekarang? berapa pesan?
GET /llms.txt                     # ringkasan buat agent (plain text)
Contoh jawaban /verify: {"address":"owokun@zenvex.edu.pl","provider":"zv","ok":true,"messages":1,"verified":true}
Cara e2e-nya: mail beneran dikirim ke tiap domain lewat Cloudflare Email Routing (verification email ke alamat tujuan) + kiriman Gmail nyata. Yang nggak pernah muncul setelah 150 detik ditandai gagal, bukan ditebak.

Resep e2e 60 detik (buat agent)

1. GET  /status                      → pilih domain dengan verified=true
2. POST /accounts {"address":"t1@vyno.me"}
                                     → {id, address, token}
3. (kirim email dari alamat luar ke address itu)
4. GET  /messages        Bearer token → tunggu member[].length > 0
5. GET  /messages/{id}   Bearer token → body ada di text + html[]
6. GET  /verify?address=t1@vyno.me    → messages harus ikut naik

Kalau langkah 4 tetap kosong > 60 detik:
  • cek /verify → ok:false / error  → provider-nya yang bermasalah
  · cek /status → provider live:false → upstream mati / sesi zenvex kadaluarsa
Penting buat agent: body pesan tidak pernah ada di GET /messages. List cuma metadata (from, subject, createdAt). Selalu lanjut ke GET /messages/{id}.