BulkMail API 文档

BulkMail 提供基于 HTTP 的邮件发送 API。所有请求和响应都使用 JSON,任何语言都能直接调用。

基础地址:https://bulkmail.dev

快速开始

  1. 注册账户,并点击验证邮件里的链接完成邮箱验证。
  2. 在控制台的「域名」页面添加你的域名,把显示的 DNS 记录添加到域名服务商。DKIM 记录生效后,域名会自动变为「已验证」。
  3. 在「API Keys」页面创建一把 Key。Key 只显示一次,请妥善保存。
  4. 发送第一封邮件:
curl -X POST https://bulkmail.dev/emails \
  -H "Authorization: Bearer mk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@acme.com>",
    "to": "user@example.com",
    "subject": "你好",
    "html": "<p>这是我的第一封邮件</p>"
  }'
建议使用子域名发信(例如 mail.acme.com),和公司日常办公邮箱分开,互不影响信誉。

认证

所有 API 请求都需要在请求头中带上 API Key:

Authorization: Bearer mk_your_api_key

API Key 等同于账户密码,请只在服务端使用,不要放在网页或 App 的前端代码里。如果泄露,在控制台删除那一把并重新创建即可。

发送邮件

POST/emails

邮件会先进入发送队列,接口立即返回邮件 ID。投递结果可以通过查询接口或 Webhook 获取。

请求参数

参数类型说明
fromstring必填。发件人,格式为 地址 或 名称 <地址>。域名必须已在你的账户中验证。
tostring | string[]必填。收件人。
ccstring | string[]抄送。
bccstring | string[]密送。
reply_tostring | string[]回复地址。
subjectstring必填。主题,不能包含换行,最长 998 个字符。
htmlstringHTML 正文。
textstring纯文本正文。html 和 text 至少填一个,两者都填时收件人的邮箱客户端会自动选择。合计不超过 512 KB。

收件人、抄送和密送的总数不能超过账户等级的上限(见限额)。

响应

HTTP/1.1 202 Accepted

{
  "id": "9db6562a-d026-4c68-ae7d-df08dd3af862",
  "status": "queued",
  "suppressed": []
}

suppressed 列出因在抑制名单中而被跳过的收件人。如果所有收件人都被抑制,status 为 suppressed,邮件不会发出。

邮件状态

状态含义
queued已进入发送队列
sent已交给邮件服务器,正在投递
delivery_delayed对方暂时拒收,正在自动重试
delivered对方邮件服务器已接收
bounced硬退信(例如地址不存在),地址已加入抑制名单
complained收件人举报为垃圾邮件
failed发送失败,例如被对方拒收或重试超时,原因见 error 字段
suppressed所有收件人都在抑制名单中,没有发出

查询邮件

GET/emails/{id}

返回邮件内容、当前状态,以及每个收件人的投递事件。

{
  "id": "9db6562a-d026-4c68-ae7d-df08dd3af862",
  "from": "Acme <hello@acme.com>",
  "to": ["user@example.com"],
  "subject": "你好",
  "status": "delivered",
  "created_at": "2026-11-08T10:24:01.000Z",
  "sent_at": "2026-11-08T10:24:02.000Z",
  "events": [
    {
      "type": "delivered",
      "recipient": "user@example.com",
      "detail": "250 2.0.0 OK",
      "created_at": "2026-11-08T10:24:03.000Z"
    }
  ]
}
GET/emails?limit=20&before={cursor}

按时间倒序列出邮件。limit 最大 100。响应中的 next_before 不为空时,把它作为下一页的 before 参数。

域名

POST/domains
{ "name": "acme.com" }

返回域名信息和需要添加的 DNS 记录:

用途类型名称说明
DKIMTXT<selector>._domainkey.acme.com必需。邮件签名公钥,生效后域名即可发信。
SPFTXTsend.acme.com强烈建议。授权平台的 IP 代表该域名发信。
退信地址MXsend.acme.com强烈建议。用于接收退信。
DMARCTXT_dmarc.acme.com建议。Gmail 等邮箱对批量发件人要求必须有。
POST/domains/{id}/verify

立即检查 DNS 记录。系统也会每 10 分钟自动检查一次;添加后 72 小时内未验证的域名会失效,需要重新添加。

GET/domains · GET/domains/{id} · DELETE/domains/{id}

Webhook

PUT/webhook
{ "url": "https://your-app.com/webhooks/mail" }

第一次设置时,响应会包含签名密钥 secret,只显示这一次。之后邮件状态每次变化,我们都会向这个地址发送 POST 请求:

{
  "type": "email.delivered",
  "created_at": "2026-11-08T10:24:03.000Z",
  "data": {
    "email_id": "9db6562a-d026-4c68-ae7d-df08dd3af862",
    "recipient": "user@example.com",
    "detail": "250 2.0.0 OK"
  }
}

事件类型

事件触发时机
email.sent邮件已交给邮件服务器
email.delivered对方邮件服务器已接收
email.delivery_delayed暂时投递失败,稍后自动重试
email.bounced硬退信,地址已加入抑制名单
email.rejected被对方拒收(例如判为垃圾邮件),或重试超时
email.complained收件人举报为垃圾邮件
email.failed发送失败(例如账户被停用)

你的服务器返回 2xx 即视为成功;否则会按指数退避重试,最多 8 次。

验证签名

每个请求都带有 X-Webhook-Timestamp(Unix 秒)和 X-Webhook-Signature 两个请求头。用签名密钥对 时间戳 + "." + 原始请求体 计算 HMAC-SHA256,与签名比对:

import crypto from "node:crypto";

export function verifyWebhook(headers, rawBody, secret) {
  const ts = headers["x-webhook-timestamp"];
  const sig = headers["x-webhook-signature"] ?? "";
  const expected = "v1=" + crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");

  // 拒绝 5 分钟以前的请求,防止重放
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh
    && sig.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

错误码

出错时响应体为 { "error": "错误说明" }。

状态码含义
400请求体不是合法的 JSON
401缺少 API Key,或 Key 无效
403发件域名未验证、邮箱未验证,或账户已被停用
404资源不存在
409资源已存在,例如域名已被添加
413邮件正文超过 512 KB
422参数不合法,例如地址格式错误、收件人过多
429超过每日或每月发送额度
500服务器内部错误,请稍后重试

限额

免费版专业版独立 IP
每日额度100 封10,000 封不限
每月额度3,000 封100,000 封不限
单封收件人10 个50 个50 个
发信 IP免费共享池优质共享池专属 IP
自动停用阈值退信率 4% / 投诉率 0.08%退信率 5% / 投诉率 0.1%退信率 5% / 投诉率 0.1%

额度按收件人数计算(一封邮件发给 3 个人算 3 封)。每日额度在 UTC 0 点重置。退信率和投诉率按近 7 天统计,超过阈值时账户会被自动停用,以保护平台上所有用户的送达率。