ManyMail API

The whole outreach flow as HTTP: register, add sender mailboxes, import contacts in bulk, broadcast, send one-off email, track progress, search replies. An AI agent can run it end to end; your own system can treat “send email” as a function call.

Base URL:https://api.hzhigzho.cn/api/v1

Every response is {"code":0,"message":"success","data":{…}}. code 0 means success.

Quick start

Five requests make one complete campaign. $API stands for the base URL above, $TOKEN for the token returned in step 1.

# 1) Register — returns a long-lived token
curl -X POST https://api.hzhigzho.cn/api/v1/agent/register \
  -H 'Content-Type: application/json' \
  -d '{"username":"my-ai-agent","password":"a-strong-password"}'

# 2) Add a sender: mailbox + app password, server guessed for you
curl -X POST https://api.hzhigzho.cn/api/v1/agent/senders \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"email":"you@gmail.com","auth_code":"app-password","from_name":"Alex"}'

# 3) Import contacts: tens of thousands at once, split every 800
curl -X POST https://api.hzhigzho.cn/api/v1/agent/contacts \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"emails":["a@example.com","b@example.com"],"group_size":800}'

# 4) Broadcast (starts immediately by default)
curl -X POST https://api.hzhigzho.cn/api/v1/agent/broadcast \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"subject":"Model A-1200 quote","content":"Hi, our quote is attached…","categories":["Group 1","Group 2"]}'

# 5) Check progress
curl https://api.hzhigzho.cn/api/v1/agent/tasks/<campaign_id> \
  -H "Authorization: Bearer $TOKEN"
An auth code is not your login password. QQ Mail, 163, Gmail and most providers require you to enable SMTP/IMAP and generate an app-specific password first. Use that to send.

Authentication

Two credentials, same behaviour on every business endpoint:

  • JWT — returned by register/login, sent as Authorization: Bearer <token>. Long-lived; fine for a one-off integration.
  • API key — starts with mmk_, revocable, usage-counted. Send as Authorization: Bearer mmk_xxx or X-API-Key: mmk_xxx. Best when you hand access to an AI agent or a partner.

GET requests also accept ?token= so a browser link can carry the credential.

Account

POST/agent/registerCreate an account, get a token
FieldTypeNotes
username requiredstringEmail, phone, or letters/digits/underscore
password requiredstringAt least 6 characters
POST/agent/loginExchange credentials for a token
curl -X POST $API/agent/login -H 'Content-Type: application/json' \
  -d '{"username":"my-ai-agent","password":"a-strong-password"}'
GET/agent/meWhat do I have, what is missing

Returns sender count, contact total, category list, recent tasks and next_steps — plain instructions the model can follow directly.

{
  "username": "my-ai-agent",
  "max_senders": 1,            // 0 = unlimited
  "sender_count": 1,
  "contact_total": 1600,
  "categories": [{"id":"…","name":"Group 1"},{"id":"…","name":"Group 2"}],
  "next_steps": ["Ready to send: POST /agent/broadcast …"]
}

Senders

You never need the SMTP host: QQ, 163, 126, Gmail, Outlook, iCloud, Alibaba and Tencent enterprise mail are built in. Custom domains are resolved through their MX record (Tencent / Alibaba / NetEase / Google / Microsoft), and only then guessed as smtp.domain.

GET/agent/presets?email=you@gmail.comPublic lookup of server settings

No login required. Returns SMTP/IMAP host, port, provider name and a hint such as “use an app password”.

POST/agent/sendersAdd a mailbox, host auto-detected
FieldTypeNotes
email requiredstringSender address
auth_code requiredstringApp password / auth code (password is accepted too)
from_namestringDisplay name
daily_limitintPer-mailbox daily cap, 0 = default
smtp_host / smtp_portstring / intOptional override when detection is wrong
imap_enabledboolSync incoming mail, default true
{"code":0,"data":{"id":"…","smtp":"smtp.gmail.com:465","imap":"imap.gmail.com:993",
 "detected_from":"preset","preset":{"provider":"Gmail","source":"preset"}}}
GET/agent/sendersList mailboxes (no secrets)

Contacts

One group holds 800 contacts by default. Import 1600 and you get “Group 1” and “Group 2”; pass category and they become “Customers-Group 1”, “Customers-Group 2”. Re-importing never duplicates — existing addresses are moved into the new group.

POST/agent/contactsBulk import with auto grouping
FieldTypeNotes
emailsstring[]Mailbox array; combine with or replace text
textstringRaw block separated by newline / comma / semicolon / space / tab
categorystringGroup name prefix; empty → Group 1, Group 2…
group_sizeintContacts per group, default 800, max 5000, -1 = no split
{"input":1600,"inserted":1600,"updated":0,"skipped":0,
 "groups":[{"category_id":"…","name":"Customers-Group 1","count":800},
           {"category_id":"…","name":"Customers-Group 2","count":800}]}
GET/agent/contacts/search?q=acme&limit=20Search contacts

Fuzzy match on email, name, company and phone. Returns trimmed records so the contact list never floods the model context.

Sending

POST/agent/sendOne-off email to specific recipients
FieldTypeNotes
to requiredstringComma separated for several recipients
subjectstringDefaults to “Hello”
content requiredstringPlain text is converted to HTML with line breaks
htmlboolSend content as-is
signaturestringAppended signature
fromstringPick a mailbox; omitted → rotated pool
POST/agent/broadcastBulk send in one request
FieldTypeNotes
subject requiredstringSubject line
content requiredstringBody, plain text converted to HTML
categoriesstring[]Group names, e.g. ["Group 1","Group 2"]
category_idsstring[]Group ids instead of names
emailsstring[]Raw addresses; imported into an “API quick send” group
startboolDefault true; false creates a draft only
batch_sizeint1 = one by one, >1 uses BCC, max 100
send_intervalintSeconds between batches, 0 = global default
htmlboolBody is HTML
{"code":0,"data":{"campaign_id":"6aea…","status":"running","recipient_count":1600,
 "next":"GET /agent/tasks/6aea… for sent / failed / opened / clicked"}}
Broadcasts send real email. While testing, pass "start": false, verify recipients and copy, then start it.

Progress

GET/agent/tasks?status=running&limit=20List campaigns
GET/agent/tasks/:idOne campaign
{"status":"running","recipient_count":1600,"progress":"42%",
 "stats":{"total":1600,"sent":672,"success":669,"failed":3,"bounced":0,"opened":184,"clicked":37}}

status: draft, running, paused, completed, failed.

GET/agent/messages/search?q=quote&email=buyer@example.comSearch received mail

Matches subject, sender and body. Add email to restrict to one counterpart. Bodies are truncated to 400 characters — enough to judge, small enough for a context window.

{"total":1,"items":[{"from":"buyer@example.com","subject":"RE: Model A-1200 quote",
 "text":"Could you send a formal quote? We need 500 units…","received_at":"2026-11-04T02:30:04Z",
 "is_read":false,"attachments":0}]}

API keys

POST/agent/keysIssue a long-lived key
GET/agent/keysList keys (prefix only)
DELETE/agent/keys/:idRevoke
curl -X POST $API/agent/keys -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"name":"my-agent"}'

# plaintext is returned exactly once
{"id":"…","prefix":"mmk_c405929c","api_key":"mmk_c40592…"}

Errors & limits

codeMeaningWhat to do
0Success—
400Bad requestRead message; it names the missing field
401No or invalid credentialLog in again or rotate the API key
403Account bannedContact support
404Not foundMake sure the id belongs to this account
500Server errorRetry once; contact us if it persists
  • Sender count is quota-limited (1 by default). Need more? Write to mark@highzo.com.
  • Each mailbox can carry its own daily_limit; rotation spreads load and lowers spam/ban risk.
  • 800 contacts per group by default; keep a single import under ~200k addresses.

Notes for AI agents

Hand a model these two URLs and it has everything:

  • https://www.hzhigzho.cn/docs/openapi.json — machine-readable schema
  • https://www.hzhigzho.cn/llms.txt — plain-text product and API summary

Recommended order — every step is idempotent and safe to retry:

GET  /agent/me                        // inspect, fill gaps
POST /agent/senders                   // mailbox + app password is enough
POST /agent/contacts                  // tens of thousands, auto grouped
POST /agent/broadcast {"start":false} // draft first
POST /agent/tasks/:id/start           // then actually send
GET  /agent/tasks/:id                 // poll progress
GET  /agent/messages/search           // read replies, follow up

Rather not write code?

Give the docs URL to any capable model — it can register, add a mailbox, import lists, send and summarise by itself.

See AI automation