跳到主要内容
abnormal.开发者

Developer docs

在 Abnormal 上开发。

Abnormal 工作区的管理 API 和 MCP 服务器:用你自己的代码或 AI Agent 管理域名、邮箱、别名、邮件列表和 Webhook,密钥按权限范围授权,事件带签名。

最近核对: · API 版本:1.8.0

API 能做什么

API 管理一个工作区的邮件服务。它能做的事,管理员在管理控制台里也都能做;有了 API,就可以交给脚本、集成或 Agent 去做。

  • 域名:验证所有权、接入域名、检查 DNS、设置 catch-all、添加别名域名。
  • 邮箱:创建、删除和恢复邮箱,修改密码、状态、容量、备注和每日发信上限。
  • 地址:转发规则、普通别名和邮件列表。
  • 合规与报表:法律保留、不活跃邮箱、各域名的存储用量和发信最多的邮箱。
  • 事件:邮箱、域名、订单、批量任务和安全事件的签名 Webhook。
  • 收发邮件:用你的域名发信、跟踪每封邮件的投递,并以 Webhook 接收邮件(需开通)。
  • Agent 邮箱:一次调用为 AI Agent 建好邮箱,读收件箱、按会话回复,并发一把只能用于这个邮箱的密钥。
  • 邮件应用:让邮箱用户通过 OAuth 授权你的应用读取他们的邮件、以他们的身份发信。
  • 投递:抑制列表、邮件追踪,以及替你读回的延迟退信和垃圾邮件投诉。
  • 客户端与连接器:TypeScript 和 Python SDK、命令行工具、n8n 和 Zapier,以及飞书、钉钉群机器人(见下方应用目录)。
  • 批量:把多项修改放进一个按顺序执行、可安全重试的请求。

申请工作区和密钥

  1. 申请工作区。 API 属于管理控制台里的一个工作区。申请工作区后会有人工审核,通过后你会作为管理员收到邀请。如果申请页提供“开发者工作区”,选它即可在确认邮箱后立即获得一个,用测试密钥试用 API。
  2. 创建密钥。 在管理控制台打开 设置 → API 密钥,创建一个密钥,只勾选代码需要的权限范围。只有管理员能创建密钥。
  3. 立即保存。 完整的密钥只在创建时显示一次。把它放在密钥管理工具或环境变量里,不要写进代码仓库、网址或日志。

密钥能做的事永远不会超过其所有者当前在控制台里的角色;吊销后,下一次请求就会被拒绝。工作区的每位管理员都能看到并吊销其他账号的密钥。

第一个请求

所有请求都发往 https://console.abnormalhq.com/api/v1,并以 Bearer 令牌的形式带上密钥。建议第一次先调用 GET /capabilities:它会返回可用的权限范围、功能和当前生效的限制。

API_BASE='https://console.abnormalhq.com/api/v1'
printf 'API key: '; stty -echo; IFS= read -r API_KEY; stty echo; printf '\n'

curl --fail-with-body "$API_BASE/capabilities" \
  -H "Authorization: Bearer $API_KEY"

用隐藏输入读入密钥,它就不会留在 shell 历史里。用完后运行 unset API_KEY。

权限范围

每个操作需要一个权限范围,在下方的接口列表里标在操作旁边。写权限不包含对应的读权限。有两个例外:POST /batch 会逐一检查其中每项操作的权限范围,GET /batch/{requestId} 只接受提交该批次的那个密钥。

  • domains:read、domains:write:域名、接入申请、DNS 与连接状态、catch-all、别名域名。
  • mailboxes:read、mailboxes:write:邮箱、回收站、发信上限和用量报表。
  • forwarders:*、aliases:*、lists:*:转发规则、普通别名和邮件列表。
  • holds:*、webhooks:*、suppressions:*、trace:read:法律保留、Webhook、抑制列表和邮件追踪。
  • agent_tasks:*:跟进任务,即以某个邮箱身份回复邮件的 Agent。
  • emails:*、inbound:*:API 发信与收信路由(需手动勾选,新密钥默认不含)。
  • agent_mailboxes:*、mail_apps:*:Agent 邮箱,以及邮箱用户可以授权的应用(同样需手动勾选)。

域名、转发、别名、邮件列表、法律保留和 Webhook 即使只读也需要管理员角色;邮箱和跟进任务的权限范围运营人员也能用,其中的读权限查看者也能用。

请求、响应与错误

  • 请求体是 JSON(Content-Type: application/json),最大 16 KiB。
  • 成功结果是 { "data": … },列表另带 pagination。创建成功返回 201。
  • 路径里的 {id} 是 API 返回的记录 ID,不是邮箱地址或域名。
  • POST /batch 按顺序执行 1 到 25 个操作,并且必须带 Idempotency-Key:用同一个键重发同一批操作,会直接返回保存的结果,不会执行两次。

所有错误的格式都一样。请根据 code 判断,不要根据 message:后者是写给人看的,内容可能会变。

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Try again in 42 seconds.",
    "details": { "retryAfter": 42, "limit": 300 }
  }
}

遇到超时、连接中断或 502 后,先读取当前状态,再决定是否重做写操作。返回 409 且错误码为 provisioning_uncertain 时,邮箱可能已经创建也可能没有:请用 details.mailboxId 查询,不要再创建一次。

频率限制

  • 按密钥计数,REST 和 MCP 合并计算。默认每分钟 300 次;当前生效的限制由 GET /capabilities 的 limits.requestsPerMinute 给出。
  • 每个响应都带 RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset 和 RateLimit-Policy。
  • 超出限制时返回 429,并用 Retry-After 给出需要等待的秒数。被拒绝的请求什么都没做,等待之后重发是安全的。
  • 同一地址发来太多无效密钥,该地址会被暂时封禁,有效密钥也会被拒绝。请修好发错密钥的客户端。

Webhook

Webhook 是一个 https 地址,工作区里发生事情时会向它发送 POST 请求,你就不用轮询。用 POST /webhooks 创建;签名密钥(whsec_…)只在这次响应里出现一次。每个工作区最多 10 个。

每次投递都在 X-Mailhq-Signature 头里签名,格式为 t=<milliseconds>,v1=<hex HMAC-SHA256>(毫秒时间戳和十六进制的 HMAC-SHA256),签名内容是时间戳、一个点和原始请求体。先校验签名,再信任请求体:

import { createHmac, timingSafeEqual } from "node:crypto";

/** rawBody: the body exactly as received; header: the X-Mailhq-Signature value. */
export function verifyMailhqWebhook(secret, rawBody, header, toleranceMs = 5 * 60_000) {
  const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
  if (!match) return false;
  const timestamp = Number(match[1]);
  if (Math.abs(Date.now() - timestamp) > toleranceMs) return false;
  const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest();
  const given = Buffer.from(match[2], "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}
  • 在 10 秒内返回 2xx,之后再处理业务。
  • 投递至少一次且不保证顺序:用事件的 id 去重。
  • 投递失败会以逐渐拉长的间隔重试约 17 小时。连续失败 3 天的 Webhook 会被停用,直到你重新启用。
  • POST /webhooks/{id}/test 会发送一个测试事件,用来检查接收端。设置 → 开发者 会列出所有 Webhook 最近的投递及接收方的回应,并可重新投递;状态页会显示 Webhook 投递是否准时。
  • 想不写接收端就在群聊里收到事件,创建 Webhook 时设 "format": "feishu" 或 "dingtalk",填写群机器人的地址(签名密钥填在 botSecret):每个事件会以一条简短消息到达。

收发邮件

用你自己的域名、通过域名自己的邮件服务器发信,跟踪每封邮件的投递结果,并把指定邮箱收到的邮件推送到 Webhook。发信功能默认关闭,需由我们为你的工作区开通并设定每日上限,请来信申请。测试密钥可以直接向沙箱发信。

  • PUT /domains/{id}/sending 为域名创建发信账号:邮件以该域名的 DKIM 密钥签名发出,退信回到这个账号,From 可以是该域名下的任意地址。
  • POST /emails 发送一封邮件(POST /emails/batch 一次最多 50 封);带上 Idempotency-Key 即可安全重试。抑制列表中的收件人会被跳过。
  • GET /emails/{id} 给出每个收件人的结果:已发出、已送达、投递延迟或退信。同样的变化也会以 email.sent、email.delivered、email.delivery_delayed、email.bounced 和 email.failed 事件推送。
  • PUT /mailboxes/{id}/inbound 开启收信路由:邮箱收到的每封邮件都会以 message.received 推送,附带纯文本正文;完整邮件和附件用 GET /mailboxes/{id}/messages/{emailId} 读取。用别名或 catch-all 可以把多个地址汇入同一个路由邮箱。
  • 如果你的套餐按发信量计费,需先在 设置 → 开发者 → 计费 中订阅,否则正式密钥发信会得到 402 billing_required。每封交给邮件服务器的邮件计一次,与收件人数量无关。
  • 管理后台的 设置 → 开发者 会显示当天的发信额度、最近的 API 调用及其错误码、每封发出的邮件和每个收件人的结果。
  • 回到域名发信账号的延迟退信和垃圾邮件投诉(反馈环报告)会被读回:收件人变为 bounced,或得到 complainedAt 和一个 email.complained 事件,并被加入抑制列表。
  • 新开通的工作区会先预热:第一天最多 50 封,此后每天翻倍,直到上限。退信或投诉过多时正式发信会自动暂停(403 email_api_paused,并推送 email.sending_paused 事件),直到我们核查。
  • 只能走 SMTP 的软件可以用 POST /domains/{id}/smtp-credentials 经域名自己的服务器发信:这个密码只能登录和发信,与 API 密钥分开创建和撤销。这样发出的邮件不是 API 邮件:没有 GET /emails 记录,也没有投递事件,计入的是发信账户在服务器上自己的每日限额,而不是工作区的发信额度。

Agent 邮箱

Agent 邮箱是你域名下一个真正的邮箱,一次调用就为 AI Agent 建好,收信路由和发信都已开启。

  • POST /agent-mailboxes 传入 domain 和 name 即可创建;GET /agent-mailboxes/{id}/messages 读取收件箱,POST …/send 发送新邮件,POST …/messages/{emailId}/reply 在原会话中回复。
  • POST /agent-mailboxes/{id}/keys 返回一把只对这个邮箱有效的密钥:交给 Agent,它就碰不到工作区里的其他任何东西。
  • Agent 收到的邮件同时以 message.received 事件推送,无需轮询。PATCH 和 DELETE …/messages/{emailId} 可把邮件标为已读或删除,GET …/attachments/{index} 可下载附件。

由邮箱用户授权的应用

你的应用可以在邮箱用户同意后读取其邮件、以其身份发信,全程看不到用户的密码:在 Abnormal 网页邮箱上使用 OAuth 2.0 授权码流程加 PKCE。

  • 用 POST /mail-apps 注册应用:名称、回调地址,以及它可以申请的权限 mail.read、mail.send 和 mail.modify。所有应用都是公开客户端:没有 client secret,必须使用 PKCE(S256)。
  • 把用户引导到网页邮箱的 /oauth/authorize;用户登录后会看到你的应用名称和申请的权限,确认即可。在 /api/oauth/token 用授权码换取访问令牌(1 小时)和每次使用都会轮换的刷新令牌。
  • 带着令牌调用 Mail API:GET /api/mail/v1/messages 和 /messages/{id} 读取、搜索邮件(…/attachments/{index} 下载附件),POST /api/mail/v1/send 以该邮箱发信,可带附件并在“已发送”中留存副本;有 mail.modify 时应用还能标记、移动和删除邮件。
  • 用户随时可以在网页邮箱设置里撤销授权,修改邮箱密码也会终止全部授权;DELETE /mail-apps/{id} 会一次终止你的应用的全部授权。

给 AI Agent 用的 MCP

同样的操作也作为工具提供在 MCP 服务器 https://console.abnormalhq.com/api/mcp 上(Streamable HTTP),密钥、权限范围和频率限制都相同。只给 Agent 它需要的权限范围。以 Claude Code 为例:

claude mcp add --transport http abnormal https://console.abnormalhq.com/api/mcp \
  --header "Authorization: Bearer $ABNORMAL_API_KEY"

支持 OAuth 登录的客户端(例如 claude.ai、Claude Desktop 和 Claude Code 上的 Claude)只需要这个地址:它们会自行注册,由管理员在授权页上批准权限,令牌会自动续期。不加请求头,客户端就会打开登录页。

应用目录

除了自己调用 API,把其他软件接入 Abnormal 的所有方式。管理控制台的 设置 → 开发者 → 集成 也列出了同样的内容。

  • Claude 及其他 MCP 客户端(可用):上文的 MCP 服务器,支持 OAuth 登录。
  • 你自己的应用,面向你的邮箱用户(可用):使用 OAuth 的邮件应用,见上文。
  • 飞书和钉钉(可用):把 Webhook 事件或你的控制台通知推送到群机器人。
  • Slack 和 Telegram(可用):把你的控制台通知发到频道或会话。
  • TypeScript SDK、Python SDK 和 `abnormal` 命令行工具(尚未发布):带类型的调用、重试、幂等键和 Webhook 校验;命令行工具将可用 brew install abnormalhq/tap/abnormal 安装。
  • n8n 和 Zapier(尚未发布):n8n 的节点和触发器,以及 Zapier 的触发器、动作和搜索。

版本与变更

  • 版本写在路径里:/api/v1。在同一版本内只做新增:新接口、新的可选字段、新的响应字段、新的枚举值和新的事件类型。你的代码应当能接受不认识的字段和值。
  • 在 v1 里删除或修改任何东西都会先公告,在规范里标为弃用,并在每个响应里带上 Deprecation 和 Sunset 头:代码会发送的内容至少提前 6 个月,只会读取的字段或值至少提前 3 个月。
  • 下方接口列表所依据的 OpenAPI 3.1 规范可以下载 JSON 文件,供代码生成器和 API 工具使用。
  • 服务状态页会显示管理 API 近 30 天的可用率(每分钟检测一次),以及故障和维护。你自己的监控可以轮询 GET https://console.abnormalhq.com/api/health,无需密钥。

测试模式

创建密钥时打开 测试密钥,就可以在不动到正式域名和邮箱的情况下开发集成。它的令牌以 mhq_test_ 开头,所有调用都发生在本工作区的沙箱里;沙箱的邮件服务器是模拟的,不会投递任何邮件,也不会查询 DNS。

  • 域名必须以工作区的专属区域 <slug>.test 结尾(例如 shop.acme.test),GET /capabilities 会在 sandbox.domainZone 里给出它。所有权验证无需 DNS 即可通过。
  • 邮箱、别名、邮件列表、转发、法律保留、批量操作和抑制列表都和正式工作区一样可用,Webhook 也会真实投递。
  • 发信无需开通:除本地部分以 bounce 开头的地址会退信外,所有收件人都会立即“送达”;发到开启收信路由的沙箱邮箱的邮件会以 message.received 推送。
  • 邮件追踪、跟进任务、传输安全、读取收到的邮件(包括 Agent 邮箱的)和邮件应用需要真实的邮件,测试密钥调用它们会得到 403 test_mode_unsupported。
  • 每个沙箱最多 5 个域名、100 个邮箱。

暂不提供

  • 未经邮箱用户授权读取未开启路由的邮箱。
  • 打开与点击跟踪:这是有意不做的,它会读取人们怎么处理自己的邮件,我们的网页邮箱也不这样做。
  • SDK 包、命令行工具以及 n8n、Zapier 的上架已经做好但尚未发布:发布后本页会给出链接。

邮箱仍然通过网页邮箱、IMAP 和 SMTP 收发自己的邮件。关于 API 的问题请发邮件到 support@abnormalhq.com。

错误码

每个错误都有一个稳定的 code。下表列出全部错误码,以及对应的状态码、含义、处理办法和能否原样重试。v1 内可能新增错误码,遇到不认识的请按 HTTP 状态码处理。说明为英文。

  • 不能:请求不改就会一直以同样方式失败。
  • 稍后:什么都没发生,等一会儿可能成功。有 Retry-After 时按它等待。
  • 先核对:结果未知或状态已变。先读取当前状态再决定,盲目重发可能把事情做两遍。

认证、请求格式与限制

错误码状态码含义处理办法重试
body_too_large413The request body is over 16 KiB (8 MiB for POST /emails and /emails/batch).Send less per request; use several requests or a batch.不能
endpoint_retired预留410The operation was retired after its announced sunset date.Use the replacement in details.replacement; see the API changelog.不能
field_retired预留422The request sent a field, query parameter or enum value retired after its sunset date.Drop it or use details.replacement; details.field names it.不能
forbidden403The key lacks a scope the operation needs, or its owner's current role no longer allows it.Create a key with the scope listed for the operation in the reference, owned by an account whose role allows it.不能
invalid_arguments400An MCP tool was called with arguments that are not a JSON object of the tool's own fields.Pass exactly the fields in the tool's input schema.不能
invalid_id400An ID in the path or body is not in the format of a record ID.Use the IDs the API returns.不能
invalid_json400The body is not valid JSON, or not a JSON object.Send a JSON object.不能
invalid_query400, 422A query parameter is unknown, malformed or out of range.Fix the parameter details names; the reference lists each operation's parameters and their ranges.不能
method_not_allowed405The path exists but not with this HTTP method.Use one of the methods in the Allow header.不能
not_found404The record in the path does not exist in this workspace (or is in the recycle bin, for mailboxes).Check the ID: paths take the record IDs the API returns, not addresses or domain names.不能
rate_limited429The key's (or the address's) rate limit is used up for this window; nothing was done.Wait Retry-After seconds, then send the same request again. Use batches for bulk changes.稍后
unauthorized401No Bearer key was sent, or the key is unknown, expired, revoked or no longer valid for its owner.Send a working key in Authorization: Bearer. Repeated invalid keys get the sending address rate-limited.不能
unknown_field400The body has a field the operation does not take.Remove it; details names it.不能
unsupported_media_type415A request body was sent without Content-Type: application/json.Send JSON with that content type.不能

域名、接入与别名域名

错误码状态码含义处理办法重试
alias_changed409The alias domain changed while the request ran.Read it again, then decide whether to repeat the request.先核对
alias_limit409The domain already has the most alias domains allowed.Remove one before adding another.不能
already_exists409The domain, alias domain or address already exists (here, or claimed by another workspace).Use the existing record, or choose another name.不能
dns_lookup_failed502The DNS lookup for the ownership check failed; nothing was changed.Try again later.稍后
domain_binding_conflict409The domain's server binding is ambiguous or changed while the request ran.Have an admin review the domain's binding, then try again.先核对
domain_changed409The domain's binding changed while the request ran.Read the domain again, then decide whether to repeat the request.先核对
domain_has_aliases409The domain still has alias domains.Remove its alias domains first.不能
domain_not_empty409The domain still has local mailboxes, so its record cannot be removed.Delete or move its mailboxes first.不能
domain_not_provisioned409The domain to import is not on the chosen server.Create it on that server first, or onboard it with /domain-onboardings.不能
domain_onboarding_conflict409The domain's onboarding has not finished, or its state does not allow this step.Read the onboarding's state and DNS status; never submit the remote provisioning twice.先核对
domain_ownership_required403The domain cannot be imported without a verified ownership onboarding in this workspace.Onboard it with /domain-onboardings instead.不能
domain_unbound409The domain is not bound to a valid mail server, so nothing on it can be changed remotely.Bind the domain to a server in the admin console.不能
invalid_domain400The domain name is missing or not a valid name.Send a lowercase domain name such as example.com.不能
invalid_domain_update400The status change does not say whether to enable or disable the domain.Send { "enabled": true } or { "enabled": false }.不能
ownership_unverified409The ownership TXT record was not found with the expected value.Publish the record the connection details show, wait for DNS, then verify again.稍后
remote_domain_delete_unavailable422Deleting a domain on the mail server is not offered.Disable the domain, then remove only the local record; the server keeps the domain and its data.不能
subscription_not_found404The mail server in subscriptionId does not exist or is not available to this workspace.Use an ID from GET /servers.不能
subscription_required400Several mail servers are configured and the request does not say which one to use.Send subscriptionId (one of GET /servers).不能
verification_expired409The alias domain's ownership value expired before it was verified.Remove the alias domain, add it again and publish the new value.不能

邮箱与发信上限

错误码状态码含义处理办法重试
invalid_daily_limit400The daily sending limit is not an integer in range.Send an integer from 0 to 1000000.不能
invalid_mailbox422The mailbox fields failed validation.Fix the fields listed in details.fields.不能
invalid_mailbox_ids400The bulk sending-limit request does not name 1–100 distinct mailbox IDs.Send between 1 and 100 distinct mailbox record IDs.不能
invalid_note400, 422The note (of a mailbox or a suppression) is too long or not text.Send at most the documented number of characters.不能
invalid_password400The password is not 8–128 characters without whitespace.Send a password that meets the rule.不能
invalid_quota400The quota is not a whole number of MB in range.Send an integer from 0 (no limit) to the documented maximum.不能
invalid_status400The status is not one of active, disabled or pending.Send one of those values.不能
invalid_username400The username (the part before @) is not valid.Use 1–64 lowercase letters, digits, ., _ or -, starting and ending with a letter or digit.不能
provisioning_uncertain409The mailbox write was sent to the server but its result is not confirmed; a pending record was kept.Look the mailbox up with details.mailboxId and verify it; do not create it again.先核对
sending_limit_unavailable422A sending limit cannot be set for this mailbox (not created on the server yet, or not supported).Create the mailbox first, or leave the limit to the server's default.不能
sending_limit_uncertain409The sending-limit write may or may not have been applied.Read the limit back (GET); repeat only the same target value, never a different one, if it is not set.先核对

别名、邮件列表、转发与 catch-all

错误码状态码含义处理办法重试
address_taken409The address is already used by a mailbox, service mailbox, alias, list, forwarder or a mailbox in the recycle bin.Choose another address.不能
external_member422The list has members outside the workspace's domains, which this workspace does not allow.Remove the outside addresses, or ask an admin to allow external members.不能
forwarding_loop409The rule forwards to itself or would close a loop with existing forwarding.Choose a different destination.不能
invalid_alias400The alias fields are invalid.Fix the username, target or description.不能
invalid_alias_target409The alias target is not an existing managed mailbox of the same domain.Point the alias at a mailbox of its own domain.不能
invalid_catch_all400The catch-all setting is invalid.Send a mailbox address of the domain, or null to reject mail to unknown addresses.不能
invalid_catch_all_destination409The catch-all destination is not an existing managed mailbox of this domain.Use one of the domain's own mailboxes.不能
invalid_forwarder400The forwarding rule's source or destination is invalid.Send one source address of the domain and one destination address.不能
invalid_list400The distribution list fields are invalid.Fix the username, description or members.不能
list_limit409The domain already has the most distribution lists allowed.Delete a list before creating another.不能
readonly_alias409This alias is a special one managed on the mail server (or a user's masked address) and cannot be changed here.Leave it as it is.不能
readonly_forwarder409This rule is not managed here (a list, alias, catch-all or custom Sieve rule).Change it where it is managed.不能
readonly_list409This list is managed on the mail server and cannot be changed here.Leave it as it is.不能
unsupported422The domain's mail server does not support this kind of address management.Use a Stalwart 1.0 or later server for the domain.不能

批量操作

错误码状态码含义处理办法重试
batch_stopped424An item of a batch that was not run because an earlier item's outcome is unconfirmed (item error.code).Verify the earlier item, then send this one again in a new batch.先核对
idempotency_conflict409This idempotency key was already used, with this key, for a different batch (or follow-up task, or messages sent through /emails).Use a new idempotency key for different content.不能
idempotency_running409A batch with this idempotency key is still running.Read its state with GET /batch/{requestId}; do not resend under another key.先核对
idempotency_uncertain409A batch with this idempotency key stopped with an unconfirmed write.Verify the affected records, then send only what is confirmed missing, in a new batch with a new key.先核对
invalid_batch400The batch body is invalid: not 1–25 operations, duplicate item IDs, an unknown operation or bad input; for POST /emails/batch, emails is not a list of 1 to 50 messages.Fix the batch; nothing in it was run or sent.不能
invalid_idempotency_key400The Idempotency-Key header is missing or not 8–128 letters, digits, - or _.Send a valid key, unique per batch.不能
request_budget_exceeded422The batch reached its request budget on the mail server and the remaining operations were stopped.Check the results, then send the rest in smaller batches.先核对

Webhook

错误码状态码含义处理办法重试
delivery_in_progress409The delivery is still waiting for its own retry.Only a delivered or dead-lettered delivery can be sent again; wait for it to settle.稍后
invalid_description400The description is longer than 200 characters.Shorten it.不能
invalid_enabled400enabled is not a boolean.Send true or false.不能
invalid_events400The events list names an unknown event type.Use the event types in the reference, or ["*"].不能
invalid_webhook400A webhook update changes nothing.Send at least one of url, description, events and enabled.不能
invalid_webhook_format400format is not standard, feishu or dingtalk, botSecret came without a bot format or is not one, or a PATCH tried to change either (they are set when the webhook is made).Send one of the formats, and a bot's signing secret only with a bot format; to change the format, make a new webhook.不能
invalid_webhook_url400The address is not https on the default port with a public host name, or, for a bot format, not that bot platform's webhook address.Use a public https address without credentials, IP literal or internal name.不能
webhook_limit409The workspace already has the most webhooks allowed (10).Delete one before creating another.不能

法律保留与抑制列表

错误码状态码含义处理办法重试
hold_not_active409The legal hold was already released or has ended.Nothing to release; place a new hold if needed.不能
invalid_address422The suppression list address is not a valid email address.Send one valid address.不能
invalid_ends_at400The hold's end date is not a future ISO 8601 time.Send a future date, or none.不能
invalid_reason400The required reason is missing or too long.Send a reason.不能

邮件追踪

错误码状态码含义处理办法重试
missing_criteria400A trace search names none of sender, recipient, Message-ID or queue ID.Give at least one of them.不能
trace_unavailable409No mail server whose logs can be read is configured.Ask an admin to connect a mail server.不能

跟进任务

错误码状态码含义处理办法重试
already_decided409This email awaiting approval was already approved or rejected.Read the task's approvals again.不能
auto_send_disabled409An admin switched automatic sending off, so every email of a task needs approval.Create the task with approval required, or ask an admin.不能
invalid_agent_task400, 422The follow-up task's fields are invalid.Fix what the message describes.不能
invalid_state409The task is not in a state that allows this (only a running task can be paused, a paused one resumed).Read the task's state first.先核对
mailbox_unavailable409The task's mailbox cannot be used (disabled, no stored password, or its domain has no mail server).Fix the mailbox, or use another one.不能
task_finished409The task has already ended.Create a new task if more follow-up is needed.不能

收发邮件、Agent 邮箱与邮件应用

错误码状态码含义处理办法重试
attachments_too_large413The attachments are over 5 MiB in all, decoded.Send smaller files, or a link to them.不能
billing_required402The workspace's plan bills sending through the API, and the workspace has no subscription that may send (none, canceled, unpaid).An admin subscribes, or fixes the payment, under Settings → Developers → Billing. details.status is the subscription's Stripe status.不能
domain_not_sending409The domain does not send through the API, so it has no sending account to make SMTP relay credentials for.Turn sending on for the domain (PUT /domains/{id}/sending) first.不能
email_api_not_approved403Sending through the API is not turned on for this workspace: a platform admin has not approved it.Ask the platform's operator to approve it (with a daily limit). Test keys can send to the sandbox meanwhile.不能
email_api_paused403Live sending is paused for the workspace: in the last 7 days (or since it was last resumed) too many recipients bounced or complained. details.reason says which, details.pausedAt since when.Find out why (a stale list, a missing unsubscribe, a stolen key) and fix it, then ask the platform's operator to resume sending. Test keys still send to the sandbox.不能
email_daily_limit429The workspace's daily allowance of messages is used up (a batch counts all of its messages).Send again after details.resetAt (midnight UTC), or ask for a higher limit. Nothing of the request was sent.稍后
from_domain_not_sending422The From address is not on a domain of this workspace that sends through the API.Turn sending on for the domain (PUT /domains/{id}/sending), or send from a domain that has it. details.index names the message in a batch.不能
invalid_agent_name400An agent mailbox's name is not a single line of at most 200 characters without quotes or angle brackets.Fix it.不能
invalid_attachments400An attachment is malformed: no filename, content that is not base64, a bad content type, or more than 10 attachments.Fix the attachments.不能
invalid_body400The message has neither text nor html, or one of them is too long.Send text, html or both, within the documented sizes.不能
invalid_from400from is not an address or Name <address>.Fix it.不能
invalid_headers400headers holds a header that is not allowed (only X- headers, List-Unsubscribe and List-Unsubscribe-Post), repeats one, or has a multi-line value.Fix the headers.不能
invalid_mail_app400A mail app needs a name, 1 to 5 redirect URIs (https on a public host, or http on the loopback) and scopes from mail.read, mail.send and mail.modify.Fix the app.不能
invalid_message_change400A change to an agent mailbox's message needs read and/or flagged, each true or false, and nothing else.Send { "read": true }, { "flagged": false } or both.不能
invalid_recipients400to, cc, bcc or replyTo holds something that is not an address, or the message has no recipient or more than 50.Fix the lists; send to more people in several messages or a batch.不能
invalid_smtp_credential400An SMTP relay credential needs a name: one line of at most 100 characters.Name what the credential is for (the application that will use it).不能
invalid_subject400subject is missing, empty, over 500 characters or not a single line.Fix it.不能
invalid_tags400tags is not an object of up to 10 names with string values.Fix the tags.不能
key_mailbox_restricted403A key restricted to one agent mailbox called something other than that mailbox's own paths.Use the key only for /agent-mailboxes/{its id} and the paths under it (not /keys, not DELETE); use another key for the rest.不能
mail_app_limit409The workspace already has 20 mail apps that are not revoked.Revoke one first (DELETE /mail-apps/{id}).不能
mailbox_unreadable409The mailbox cannot be read for inbound routing: it is not active, or has no stored password.Use an active mailbox with a stored password (created through the console or the API).不能
nothing_to_reply_to422The message has no Reply-To or From address other than the agent's own.Send a new message with POST /agent-mailboxes/{id}/send instead.不能
smtp_credential_limit409The domain already has 10 SMTP relay credentials in use.Revoke one you no longer need (DELETE /domains/{id}/smtp-credentials/{credentialId}).不能

测试模式

错误码状态码含义处理办法重试
sandbox_domain_zone422A test key asked for a domain outside its sandbox's zone (<workspace slug>.test).Use a name under the zone GET /capabilities reports in sandbox.domainZone.不能
test_mode_unsupported403A test key called an endpoint that needs real mail (message trace, follow-up tasks, transport security).Use a live key for it. GET /capabilities lists these paths under sandbox.unsupportedPaths.不能

邮件服务器与服务可用性

错误码状态码含义处理办法重试
service_unavailable503The service or its mail server configuration is unavailable.Try again later; read the current state before repeating a write.先核对
upstream_error502The mail server refused or failed the request.Read the current state before repeating a write; try reads again later.先核对

接口列表

v1 的全部操作,按功能分组,并标出所需的权限范围。路径相对于 API 地址;请求和响应的结构见规范。接口说明为英文。

Capabilities

Boundaries of the current API.

操作说明权限范围
GET/capabilities
Get API capabilitiesdomains:read

Domain onboarding

Provision a domain after verifying ownership, or look up / cancel pending requests.

操作说明权限范围
GET/servers
List available mail serversdomains:read
GET/domain-onboardings
List domain onboarding requestsdomains:read
POST/domain-onboardings
Request verification and provisioning of a domaindomains:write
GET/domain-onboardings/{id}
Get a domain onboarding requestdomains:read
DELETE/domain-onboardings/{id}
Cancel an unsubmitted domain requestdomains:write
POST/domain-onboardings/{id}/verify
Verify TXT and provision the domaindomains:write
POST/domain-onboardings/{id}/refresh
Regenerate the ownership verification recorddomains:write

Domains

Import existing Stalwart domains and manage local records.

操作说明权限范围
GET/domains
List domainsdomains:read
POST/domains
Import an existing domaindomains:write
GET/domains/{id}
Get a domaindomains:read
DELETE/domains/{id}
Remove the local domain recorddomains:write
GET/domains/{id}/connection
Get domain connection and DNS statusdomains:read
PUT/domains/{id}/status
Enable or disable a domaindomains:write
GET/domains/{id}/catch-all
Get a domain's catch-alldomains:read
PUT/domains/{id}/catch-all
Set or clear a domain's catch-alldomains:write
GET/domains/{id}/domain-aliases
List a domain's alias domainsdomains:read
POST/domains/{id}/domain-aliases
Request an alias domaindomains:write
GET/domains/{id}/domain-aliases/{aliasId}
Get an alias domain and its DNS statusdomains:read
DELETE/domains/{id}/domain-aliases/{aliasId}
Remove an alias domaindomains:write
POST/domains/{id}/domain-aliases/{aliasId}/verify
Verify ownership and add the alias domaindomains:write
POST/domains/{id}/transport-security
Verify and enable domain transport securitydomains:write

Mailboxes

Manage mailboxes; passwords and redemption-code links are never returned.

操作说明权限范围
GET/mailboxes
List mailboxesmailboxes:read
POST/mailboxes
Create a mailboxmailboxes:write
GET/mailboxes/{id}
Get a mailboxmailboxes:read
DELETE/mailboxes/{id}
Delete a mailboxmailboxes:write
GET/mailboxes/deleted
List deleted mailboxesmailboxes:read
POST/mailboxes/{id}/restore
Restore a deleted mailboxmailboxes:write
PUT/mailboxes/{id}/password
Change mailbox passwordmailboxes:write
PUT/mailboxes/{id}/status
Change mailbox statusmailboxes:write
PUT/mailboxes/{id}/quota
Change mailbox quotamailboxes:write
POST/mailboxes/sending-limits
Set daily sending limits for multiple mailboxesmailboxes:write
GET/mailboxes/{id}/sending-limit
Get a mailbox's daily sending limitmailboxes:read
PUT/mailboxes/{id}/sending-limit
Set a mailbox's daily sending limitmailboxes:write
PUT/mailboxes/{id}/note
Change mailbox notemailboxes:write

Forwarders

Forwarding rules between single regular email addresses.

操作说明权限范围
GET/domains/{id}/forwarders
List forwarding rulesforwarders:read
POST/domains/{id}/forwarders
Create a forwarding ruleforwarders:write
DELETE/domains/{id}/forwarders/{forwarderId}
Delete a forwarding ruleforwarders:write

Aliases

Regular aliases delivering to managed mailboxes on the same domain.

操作说明权限范围
GET/domains/{id}/aliases
List regular aliasesaliases:read
POST/domains/{id}/aliases
Create a regular aliasaliases:write
GET/domains/{id}/aliases/{aliasId}
Get a regular aliasaliases:read
PATCH/domains/{id}/aliases/{aliasId}
Update a regular aliasaliases:write
DELETE/domains/{id}/aliases/{aliasId}
Delete a regular aliasaliases:write

Lists

Distribution lists: an address of a domain that delivers to all of its members.

操作说明权限范围
GET/lists
List distribution listslists:read
POST/lists
Create a distribution listlists:write
GET/lists/{listId}
Get a distribution listlists:read
PATCH/lists/{listId}
Update a distribution listlists:write
DELETE/lists/{listId}
Delete a distribution listlists:write

Legal holds: a held mailbox cannot be deleted permanently or purged. A hold preserves the mailbox, not individual messages.

操作说明权限范围
GET/holds
List legal holdsholds:read
POST/holds
Place a legal holdholds:write
GET/holds/{id}
Get a legal holdholds:read
POST/holds/{id}/release
Release a legal holdholds:write

Reports

Usage reports: inactive mailboxes, storage per domain, top senders. Computed from this app's own records.

操作说明权限范围
GET/reports/inactive-mailboxes
Inactive mailboxesmailboxes:read
GET/reports/storage
Storage per domainmailboxes:read
GET/reports/top-senders
Top senders by daily sending-limit usagemailboxes:read

Batch

Manage domains, mailboxes and aliases in ordered batches with persisted idempotent results.

操作说明权限范围
POST/batch
Batch-manage domains, mailboxes and aliases按每项操作mailboxes:write, domains:write, aliases:write
GET/batch/{requestId}
Get batch execution status仅限创建该批次的密钥

Agent tasks

An agent follows up email as a given mailbox: it starts from a prompt, sends fully autonomously by default, and continues when replies arrive.

操作说明权限范围
GET/agent-tasks
List follow-up tasksagent_tasks:read
POST/agent-tasks
Create a follow-up taskagent_tasks:write
POST/agent-tasks/batch
Create a follow-up task for each of several mailboxesagent_tasks:write
GET/agent-tasks/{id}
Get a follow-up taskagent_tasks:read
PATCH/agent-tasks/{id}
Change a follow-up task's limitsagent_tasks:write
GET/agent-tasks/{id}/events
Fetch the task timelineagent_tasks:read
GET/agent-tasks/{id}/threads
List the conversations a task followsagent_tasks:read
POST/agent-tasks/{id}/threads
Hand a conversation to a taskagent_tasks:write
POST/agent-tasks/{id}/messages
Message the agentagent_tasks:write
POST/agent-tasks/{id}/pause
Pause a follow-up taskagent_tasks:write
POST/agent-tasks/{id}/resume
Resume a follow-up taskagent_tasks:write
POST/agent-tasks/{id}/cancel
Cancel a follow-up taskagent_tasks:write
GET/agent-tasks/{id}/approvals
List emails awaiting approvalagent_tasks:read
POST/agent-tasks/{id}/approvals/{approvalId}
Approve or reject an emailagent_tasks:write

Webhooks

Signed event webhooks: mailbox, domain, order, bulk job and security events POSTed to your https address, with retries and a delivery log.

操作说明权限范围
GET/webhooks
List webhookswebhooks:read
POST/webhooks
Create a webhookwebhooks:write
GET/webhooks/{id}
Get a webhookwebhooks:read
PATCH/webhooks/{id}
Update a webhookwebhooks:write
DELETE/webhooks/{id}
Delete a webhookwebhooks:write
GET/webhooks/{id}/deliveries
List a webhook's recent delivery attemptswebhooks:read
POST/webhooks/{id}/deliveries/{deliveryId}/redeliver
Send a delivery's event againwebhooks:write
POST/webhooks/{id}/test
Send a test eventwebhooks:write

Trace

Message trace: the delivery timeline of messages sent or received by the workspace's domains, read from the mail server's logs and queue. Every call is recorded in the activity log.

操作说明权限范围
GET/trace
Trace messagestrace:read

Emails

Send mail from the workspace's domains through their own mail server, and follow each message's delivery. Off until a platform admin approves the workspace; test keys send to the sandbox.

操作说明权限范围
GET/emails
List sent messagesemails:read
POST/emails
Send a messageemails:write
POST/emails/batch
Send several messagesemails:write
GET/emails/{id}
Get a message's delivery statusemails:read
GET/sending
Get the workspace's sending stateemails:read
GET/domains/{id}/sending
Get a domain's sending stateemails:read
PUT/domains/{id}/sending
Turn sending on for a domainemails:write
DELETE/domains/{id}/sending
Turn sending off for a domainemails:write
GET/domains/{id}/smtp-credentials
List a domain's SMTP relay credentialsemails:read
POST/domains/{id}/smtp-credentials
Make an SMTP relay credentialemails:write
DELETE/domains/{id}/smtp-credentials/{credentialId}
Revoke an SMTP relay credentialemails:write

Inbound

Inbound routing: mail that chosen mailboxes receive is POSTed to webhooks as message.received, and can be read through the API.

操作说明权限范围
GET/inbound-routes
List inbound routesinbound:read
GET/mailboxes/{id}/inbound
Get a mailbox's inbound routeinbound:read
PUT/mailboxes/{id}/inbound
Turn inbound routing on for a mailboxinbound:write
DELETE/mailboxes/{id}/inbound
Turn inbound routing off for a mailboxinbound:write
GET/mailboxes/{id}/messages/{emailId}
Read a received messageinbound:read
GET/mailboxes/{id}/messages/{emailId}/attachments/{index}
Download an attachment of a received messageinbound:read

Agent mailboxes

A mailbox an AI agent reads and sends from, made in one call, and a key restricted to just that mailbox for the agent itself.

操作说明权限范围
GET/agent-mailboxes
List agent mailboxesagent_mailboxes:read
POST/agent-mailboxes
Create an agent mailboxagent_mailboxes:write
GET/agent-mailboxes/{id}
Get an agent mailboxagent_mailboxes:read
DELETE/agent-mailboxes/{id}
Delete an agent mailboxagent_mailboxes:write
GET/agent-mailboxes/{id}/messages
List an agent's inboxagent_mailboxes:read
GET/agent-mailboxes/{id}/messages/{emailId}
Read a message an agent receivedagent_mailboxes:read
PATCH/agent-mailboxes/{id}/messages/{emailId}
Mark a message an agent received read or flaggedagent_mailboxes:write
DELETE/agent-mailboxes/{id}/messages/{emailId}
Delete a message an agent receivedagent_mailboxes:write
GET/agent-mailboxes/{id}/messages/{emailId}/attachments/{index}
Download an attachment of a received messageagent_mailboxes:read
POST/agent-mailboxes/{id}/messages/{emailId}/reply
Reply to a message as the agentagent_mailboxes:write
POST/agent-mailboxes/{id}/send
Send a message as the agentagent_mailboxes:write
POST/agent-mailboxes/{id}/keys
Create a key for one agent mailboxagent_mailboxes:write

Mail apps

Third-party apps a workspace registers so its mailbox users can authorize them, with OAuth on WebMail, to read their mail or send as them through WebMail's Mail API.

操作说明权限范围
GET/mail-apps
List mail appsmail_apps:read
POST/mail-apps
Register a mail appmail_apps:write
GET/mail-apps/{id}
Get a mail appmail_apps:read
DELETE/mail-apps/{id}
Revoke a mail appmail_apps:write

Suppressions

The workspace's suppression list: recipients that hard-bounced repeatedly or were added by hand. Advisory: nothing is refused at submission; check an address before sending to it.

操作说明权限范围
GET/suppressions
List suppressed recipientssuppressions:read
POST/suppressions
Suppress a recipientsuppressions:write
GET/suppressions/{address}
Check whether a recipient is suppressedsuppressions:read
DELETE/suppressions/{address}
Remove a recipient from the suppression listsuppressions:write