跳转到内容

Base URL 为 https://api.proxymail.qa/v1,使用 Authorization: Bearer qa_…。所有端点都在 REST API 参考文档 中,由 OpenAPI 描述 生成。

在用 JavaScript 或 TypeScript?npm 上的 proxymail-qa 包封装了这些调用,并带有类型。

  1. 使用你要测试的项目中的 API 密钥。

  2. 创建一个带人物设定和密码的地址,然后用它在你的应用中注册。

    curl -X POST https://api.proxymail.qa/v1/addresses \
    -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \
    -d '{"label": "Sign-up test", "persona": true, "password": true}'
  3. 然后等待验证码。它会自动返回。

    curl "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/code" \
    -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"
    {
    "type": "code",
    "subtype": "otp",
    "value": "986512",
    "service": "Spotify",
    "received_at": "2026-10-12T10:00:03Z",
    "expires_at": "2026-10-12T10:10:03Z"
    }
方法和路径 作用
GET /v1/project 密钥所属的项目、它的邮件域名和限制
PATCH /v1/project 修改项目的 name、website、environments、email_prefix 或 mail_domain,与项目设置中的操作相同。需要项目负责人,或组织所有者或管理员;每次修改都会记入活动日志
POST /v1/project/dev-hosts 将 localhost:3000 这样的主机标记为开发环境(host),这样指向它的链接能通过邮件检查
POST /v1/addresses 创建地址(label、name、persona、password、lifetime_days、domain、age 或 date_of_birth、country,以及使用位置:site 和 environment)
POST /v1/form-values 注册表单中每个字段该输入或选择什么,取自为此新建的地址或之前创建的地址(详情)
GET /v1/addresses 列出地址(最新的在前),或查找之前创建的地址(tag、country、min_age、max_age、has_password、search、site、environment)
GET / PATCH / DELETE /v1/addresses/{address} 读取、更新(first_name、last_name、username、date_of_birth、country、label、notes、tags)或删除某个地址
GET /v1/addresses/{address}/code 等待验证码(timeout、service、since;unused=true 会跳过已用过的验证码;type=coupon 用于优惠券或促销码,附带折扣和有效期)
GET /v1/addresses/{address}/link 等待链接(type:verify、magic_signin、invite、reset 或 any;unsubscribe 或 list_unsubscribe 用于 退订链接;app=true 用于打开移动应用的链接)
GET /v1/addresses/{address}/count 统计已到达的邮件(since、service、type:code 或 link、subtype、category),每封邮件只给出摘要,不含内容
GET /v1/addresses/{address}/none 检查没有邮件到达:最多保持 30 秒(timeout),没有邮件则返回 200,一旦有邮件到达立即返回 409;category=marketing 只关注优惠和新闻邮件,until 可在多次调用之间保持一个最长 1 小时的窗口
GET, PUT /v1/addresses/{address}/consent Labs。 地址在注册时同意了什么(营销邮件、预先勾选的复选框、双重确认),用于各国的营销规则
POST /v1/addresses/{address}/coupon-result Labs。 结账时如何处理优惠券(accepted、rejected 或 error,附带应用的提示信息),以及 coupon_result 判定
POST /v1/addresses/{address}/sequence Labs。 欢迎系列邮件或定时营销邮件:每封邮件在 since 之后是否按顺序、按时到达
GET /v1/addresses/{address}/offers Labs。 每个发件人的优惠券,以及每张发给了地址的哪个版本(原样、大写、+tag):一个人收到两次优惠时为 reused
GET, POST, DELETE /v1/addresses/{address}/unsubscribed Labs。 退订监测:POST 记录地址已退订某个发件人,DELETE 记录它重新订阅,GET 列出之后收到的营销邮件
GET /v1/addresses/{address}/checks 检查一封邮件:SPF、DKIM 和 DMARC,本地链接和普通 http 链接,链接指向哪里(expect_host),写明的过期时间,优惠券何时截止,纯文本部分,大小,纯图片邮件,指定短语(expect_text),语言(expect_language),到达有多快(expect_within)以及附件(expect_attachment)。只返回判定结果
GET /v1/addresses/{address}/messages 邮件及其中的验证码和链接;include=text 附加文本,include=html 附加 HTML,include=text,html 两者都附加
GET /v1/addresses/{address}/variants 用于测试注册表单的特殊地址版本:能送达收件箱的、无法送达的,以及无效的(详情)
GET /v1/variants/names 用于表单姓名字段的刁钻姓名(field、max_length):应接受的、应拒绝的、应作为文本保存的 HTML 或 SQL,以及各应用处理不一的(详情)
GET /v1/variants/passwords 符合字段规则的刁钻密码(min_length、max_length、symbols):最短和最长长度、每类符号、空格、非 ASCII 字符,以及各应用处理不一的(详情)
GET /v1/addresses/{address}/delivery 检查一个地址:收到了什么、我们拒收了什么、Cloudflare 的邮件服务器拒绝了什么及原因,以及一条提示
GET /v1/addresses/{address}/password 已保存的密码,仅限有读取权限的密钥
POST /v1/addresses/sign-in 用于登录的已保存测试账号及其密码,一次调用即可获取(site、environment、tags),仅限有密码读取权限的密钥(详情)
PUT /v1/addresses/{address}/password 保存测试账号当前使用的密码(password,或省略以自动生成一个,只返回一次);任何密钥都可以保存
POST /v1/addresses/checkout 从账号池中借出一个账号:符合筛选条件(tags、country、min_age、max_age、has_password、search、site、environment)且没有被别人占用,借用 minutes(详情)
POST /v1/addresses/{address}/extend, POST /v1/addresses/{address}/return 延长借用,或归还(token;归还时,add_tags 和 remove_tags 会记录它的状态)
POST /v1/runs 开始一次运行(name、source、meta):在 Proxy-Mail-QA-Run 请求头中带上运行 id 的调用会添加它们看到的内容(详情)
GET /v1/runs, GET /v1/runs/{id} 列出运行,或读取某次运行及其证据;format=markdown 返回报告
POST /v1/runs/{id}/notes, POST /v1/runs/{id}/tests, POST /v1/runs/{id}/finish 添加你自己的一行记录(text、failed)、一条测试结果或汇总,或结束运行(failed)

标有 Labs 的端点属于营销和优惠邮件,这是一项 Labs 功能,由密钥的创建者在应用的 Account settings → Labs 中开启。开启前,它们会返回 403 forbidden 并附带说明此情况的消息。

注册某项优惠后,用 type=coupon 等待它的优惠券(any 表示优惠券或登录验证码)。优惠邮件常常要几分钟才到,所以要等久一点或再请求一次;使用 since 时,after_seconds 会说明邮件在它之后多久到达:

curl "https://api.proxymail.qa/v1/addresses/$ADDRESS/code?type=coupon&since=$SIGNED_UP&timeout=30" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"
# { "value": "WELCOME20", "subtype": "coupon", "discount": "20% off", "valid_until": "2026-10-31", "after_seconds": 212, … }

discount 是我们对优惠内容的解读(20% off、$10 off、Free shipping、Buy one, get one),valid_until 是邮件中写明的截止日期(“有效期至 10 月 31 日”、“10 月 31 日截止”、“7 天内有效”)。邮件没有写明时,各项为 null。不带类型的等待会忽略优惠券,因此登录测试不会误取优惠码。要检查它是否及时到达,如果到得晚了,GET /checks?since=$SIGNED_UP&expect_within=5m 会让 arrival_time 判定失败。

每人一次优惠。 发往 name+promo1@… 和 NAME@… 的邮件会进入同一个收件箱,因此很适合用作一次性优惠或欢迎优惠的“第二位顾客”。先用原样地址注册,再用 GET /variants 中的版本注册,等待每张优惠券,然后调用 GET /v1/addresses/{address}/offers:它会列出每个发件人的优惠券,以及每张发给了哪个版本(plain、capitals 或 +tag);两个或更多版本都收到时为 reused,共用同一个优惠码时为 same_code。只要有优惠被重复发放,passed 就为 false,并且第一张之后的每张优惠券都会让 /checks 中的 offer_reuse 判定失败。这里无法测试 Gmail 的点号技巧:在这些域名上,加一个点就是另一个地址。

结账时。 用你自己的工具使用优惠券,只在 staging 环境中使用测试支付,然后记录应用的处理结果:

curl -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/coupon-result" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \
-d '{"result": "rejected", "code": "WELCOME20", "message": "Code already used", "one_time": true}'

result 是 accepted、rejected 或 error;code 默认是该地址最新的优惠券。每次尝试都会保留,返回结果中的 check 就是 coupon_result 判定:以下情况会判定失败:优惠券在截止日期后被接受、已发给该地址另一个版本的优惠被接受,或在 one_time 时被接受两次;出现错误或无理由拒绝时会给出警告。如果关联了运行,判定失败会让运行失败。

当表单要求填写的不只是邮箱时,发送页面上每个字段的描述,就能得到每个字段该输入或选择什么。这些值取自为该表单新建的地址:表单要求出生日期时,会生成一个(默认成年,除非你传入 age),国家取自表单下拉列表中的选项(或 country),密码符合密码字段的 min_length、max_length 和 pattern,保存在地址上,并且只在这次返回结果中显示。传入 address 可改用之前创建的地址(不会返回它已保存的密码)。

curl -s -X POST https://api.proxymail.qa/v1/form-values -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \
-H "Content-Type: application/json" -d '{
"fields": [
{ "id": "email", "type": "email", "label": "Email" },
{ "id": "user", "name": "username", "label": "Username", "max_length": 15 },
{ "id": "pw", "type": "password", "label": "Password", "min_length": 12 },
{ "id": "dob", "type": "text", "label": "Date of birth", "placeholder": "DD/MM/YYYY" },
{ "id": "country", "type": "select", "label": "Country", "options": [{ "value": "de", "label": "Germany" }, { "value": "uk", "label": "United Kingdom" }] },
{ "id": "terms", "type": "checkbox", "label": "I agree to the terms" }
]
}'
# { "address": "k7f2x9ab12cd@mailclub.dev", "created": true, "password": "…", "persona": {…},
# "values": [ { "id": "email", "kind": "email", "value": "k7f2x9ab12cd@mailclub.dev" },
# { "id": "dob", "kind": "date_of_birth", "value": "04/05/1990" },
# { "id": "country", "kind": "country", "value": "uk" },
# { "id": "terms", "kind": "other", "value": null, "note": "Not a sign-up detail Proxy Mail QA fills: fill it yourself, or leave it." }, … ] }

每个字段可以有 id(由你提供,会和值一起返回)、type(或 select)、name、label、placeholder、autocomplete、group(它所在分组的说明,比如日、月、年下拉列表上方的“出生日期”)、pattern、min_length、max_length 和 options(value 和 label)。日、月、年字段各自得到对应部分,日期选择器得到 YYYY-MM-DD,手动输入的日期使用标签或占位文字写明的格式(没有说明时使用美国顺序和 note)。Proxy Mail QA 从不查看或填写页面:由你来填,然后照常等待验证码或链接。

凡是 API 接受地址的地方,也可以使用变体:大写,或加号标签,比如 k7f2x9ab12cd+o'brien@mailclub.dev(在路径中需 URL 编码)。操作作用于原样地址,而针对 +tag 变体的等待或邮件列表只会找到发往该变体的邮件。参见 特殊的邮箱地址。

表单的其他字段也同样处理。GET /v1/variants/names?field=first_name&max_length=50 将姓名分为四组:accept(真实的人名:O'Brien、Smith-Jones、José、Straße、李、Мария、محمد、สมชาย、单个字母、正好达到最大长度)、reject(超出最大长度一个字符、空值、只有空格)、store_as_text(<b>Jane</b>、script 标签、SQL 注入、{{7*7}}:应用应按输入原样保存和显示,绝不执行)和 decide(数字、符号、emoji、前后带空格、组合与分解形式的重音字符,以及未给出最大长度时的 64、128 和 255 个字符:各应用处理不一,请记下应用的处理方式)。GET /v1/variants/passwords?min_length=8&max_length=64&symbols=true 对密码做同样处理:accept(正好达到最短和最长长度、每类符号(包括引号、反斜杠和空格)、非 ASCII 字母)、reject 和 decide(只有数字、只有小写字母、开头或结尾有空格、emoji,以及在最大长度允许时前 72 字节相同的一对密码,bcrypt 只处理到这里)。每次调用都会生成新密码。每个条目都有 tests(检查什么)和 value(输入什么)。参见 特殊的姓名和密码。

添加 "age": "under13"(或 adult、over21、under18、under15、any、turns18tomorrow、turned18today)可为人物设定生成该范围内的出生日期,也可以用 "date_of_birth": "2012-05-01" 传入你自己的日期。它以 persona.date_of_birth 返回,格式为 YYYY-MM-DD,且仅当地址有出生日期时返回。

GET /v1/addresses 支持筛选,测试或智能体可以回到之前创建的账号,而不必再新建一个。所有给出的筛选条件都必须匹配:

  • tag:重复使用,或用逗号分隔多个标签,表示需要全部匹配
  • country:人物设定的国家,比如 GB
  • min_age 和 max_age:按人物设定的出生日期计算的当前周岁(max_age=17 可找出未满 18 岁的);没有出生日期的地址不匹配
  • has_password:true 或 false,表示是否保存了密码
  • search:地址、标签、备注、姓名或用户名的一部分
  • site 和 environment:使用位置,即主机或页面 URL(site=staging.example.com)和名称(environment=staging)。在创建地址时设置;Chrome 扩展也会记录它们,应用会在每个账号上显示
curl "https://api.proxymail.qa/v1/addresses?tag=age-gate&country=GB&max_age=17&has_password=true" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"

POST /v1/addresses/sign-in 会选出用于登录的已保存测试账号,并连同密码一起返回,与 Chrome 扩展的登录列表一样:选择已保存密码、带有所有给定标签、未被任何测试借出的账号,优先选在这个 site 上用过的,其次是最近收到邮件的。带上 site 时,尚未在任何地方使用过的账号会被标记为在该处使用。它需要有读取已保存密码权限的密钥,每次显示密码都会记入日志,没有合适的账号时返回 404。

curl -s -X POST https://api.proxymail.qa/v1/addresses/sign-in -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \
-H "Content-Type: application/json" -d '{"site": "staging.example.com", "tags": ["verified"]}'
# { "address": "…", "password": "…", "persona": {…}, "site": "staging.example.com", "used_on_site": true, … }

要测试邮件模板(文案、按钮、页脚),可以向邮件端点请求其内容:GET /v1/addresses/{address}/messages?include=html 附加每封邮件的 HTML,include=text 附加纯文本。HTML 是邮件到达时清理过的副本:脚本、框架和媒体会被移除,表单控件会被解除包装,除 web、mailto: 和 tel: 之外的链接会被去掉(myapp:// 这样的应用链接仍保留在验证码和链接中),最大 200 KB。内容由发件人编写,请视为不可信内容。通过 MCP 连接的智能体永远拿不到邮件内容,只能拿到验证码或链接。

添加 "country": "GB"(任意 ISO 3166-1 alpha-2 代码,大小写均可),用于询问居住地的注册表单,或测试基于位置的内容。它以 persona.country 返回,为大写,且仅当地址有国家时返回。用 PATCH 和 "country": null 修改或清除。通过 MCP 连接的智能体也会向 create_address 和 update_address 传入 country。

等待会返回过去 15 分钟内收到的最新匹配验证码或链接,或最多保持 30 秒(timeout,0 到 30)直到有一个到达,否则返回 404。传入 since 可忽略更早的内容,比如你的测试提交表单的时间。用 service=spotify 按发件人筛选。

一旦某次等待返回了验证码或链接,或者有人在应用或 Proxy Mail QA for Chrome 中打开、复制或填入了它,它就算已使用。应用会将它显示为已使用,等待时加上 unused=true 会跳过它:适用于不更改 since 就要求第二个验证码的测试。

检查邮件没有到达,或只到达一次

Section titled “检查邮件没有到达,或只到达一次”

有些测试在什么都没发生时才算通过:已退订的用户收不到新闻邮件,一分钟内第二次重置请求被拒绝,注册只发送一封验证邮件而不是两封。

  • GET /v1/addresses/{address}/none?since=…&timeout=20 会像等待一样保持连接。如果没有匹配的邮件到达,它返回 200,带有 "arrived": false 和 until(它监视到的时间)。一旦有邮件到达,它就返回 409,带有 "error": "email_arrived"、count 以及第一封邮件的时间、服务和类型,因此 curl --fail 会让该步骤失败。把 since 设为操作发生的时间,这样在调用之前到达的邮件也会计入;不设的话,它从请求时开始监视。如需更长时间,加上 until(最多到 since 之后 60 分钟),并在响应显示 "done": false 时用相同的 since 和 until 再次调用;seconds_left 表示剩余时间。
  • GET /v1/addresses/{address}/count?since=… 会立即返回 count 和前 50 封邮件,按时间从旧到新。要检查“恰好一封”,请在等待第一封邮件返回、并留出重复邮件到达的时间之后再计数。

两者都像等待一样过滤:service,以及 type=code(优惠券以外的验证码)或 type=link(验证、魔法登录、邀请和重置链接),并可用 subtype(例如 type=link&subtype=reset)和 category 进一步缩小范围。对于“已退订的用户收不到新闻邮件”,使用 category=marketing,这样仍应到达的收据或密码重置邮件不会导致失败。+tag 变体只统计它自己的邮件。两者都不返回邮件内容:每封邮件只以 received_at、service 以及其主要验证码或链接的 type 和 subtype 概括。Node.js 客户端 提供 countEmails 和 expectNoEmail,Python 辅助工具 提供 count_emails 和 expect_no_email。

每封到达的邮件都会被赋予一个类型,并附上原因:

category 条件
auth 其主要验证码或链接用于登录、验证、重置密码或邀请,或其主题表明如此
transactional 其主题像收据、订单、付款、预订或安全通知,或附有发票 PDF 或日历邀请;以及任何不属于登录或营销的邮件
marketing 它有 List-Unsubscribe、List-Unsubscribe-Post 或 List-Id 头、Precedence: bulk、优惠券、促销主题或退订链接

收据优先于营销是有意为之:带有“下次 9 折”验证码的收据仍是收据。/count、/none 和 /checks 接受 category,/count 给出每封邮件的 category、gap_seconds(距上一封的时间)及其附件的类型、扩展名和大小。在引入类型之前到达的邮件没有类型。

邮件的退订链接以值的形式保存:GET /link?type=unsubscribe 给出邮件自身的退订或邮件偏好链接,type=list_unsubscribe 给出 List-Unsubscribe 头中的链接,即 Gmail 的退订按钮使用的链接。要实现该按钮的效果,请用你自己的工具向它 POST List-Unsubscribe=One-Click。Proxy Mail QA 从不打开其中任何一个。

附件只保存每个文件的名称、类型和大小,从不保存文件本身。缺少附件时 GET /checks?expect_attachment=.pdf(可重复使用以指定更多)会让 expected_attachment 失败;模式可以是类型(application/pdf、image/*)、扩展名(.ics)或带 * 的文件名(invoice-*.pdf)。/messages 会连同名称列出它们。

要测试退订是否生效,请用你自己的工具退订该地址:打开邮件的退订链接(GET /link?type=unsubscribe),或向头中的链接 POST List-Unsubscribe=One-Click(type=list_unsubscribe)。然后记录下来:

curl -X POST "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/unsubscribed" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \
-d '{"email_id": "…", "at": "2026-10-19T10:02:00Z"}'

email_id 是你退订的那封邮件;也可以用 service(名称或域名,例如 acme.com),两者都不提供时,以最新的营销邮件为准。at 可把时间往前设;重复标记没有影响。grace(例如 2h 或 1d,最多 30 天)允许已在途中的营销邮件通过:在此期间内的邮件不计入。从那以后,该发件人的任何营销邮件都会让 /checks 中的 after_unsubscribe 失败,无论隔多久,并显示在 Reports 仪表板上。收据、密码重置和登录邮件从不计入。GET /unsubscribed 列出每个监视及其之后到达的邮件(after_unsubscribe:ID、时间和 after_seconds,从不包含内容)以及 passed;配合运行时,检查失败会让运行失败。DELETE /unsubscribed?service=acme.com 记录该地址重新订阅。

要在测试内检查,请触发另一次营销活动,并让 GET /none?category=marketing&since=…&until=… 保持打开几分钟。

对于欢迎系列或滴灌营销,POST /v1/addresses/{address}/sequence 检查每封邮件是否按顺序、按时到达:

curl -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/sequence" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \
-d '{"since": "2026-10-01T10:00:00Z", "steps": [{"subject": "Welcome"}, {"subject": "tips", "after": "1d"}, {"category": "marketing", "after": "7d", "within": "12h"}]}'

每个步骤可以指定其 subject 中的短语、其 category、其 type(验证码或链接)、after(应何时到达,从 since 算起,最多 90 天)和 within(偏差多少仍算通过;默认为 after 的十分之一,至少 10 分钟)。每个步骤取上一步之后第一封符合的邮件,并返回 pass、early、late、missing 或 waiting(尚未到期),附带 received_at 和 off_seconds。只要有步骤在等待,done 就为 false,请稍后再检查。主题在服务器上匹配,从不返回。

记录账号注册时同意了什么,/checks 会按该角色所在地(其 country,在创建地址时设置)的规则评判每封营销邮件:

curl -X PUT "https://api.proxymail.qa/v1/addresses/$ADDRESS/consent" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Content-Type: application/json" \
-d '{"marketing": "declined", "prechecked": false}'

marketing 为 opted_in 或 declined,prechecked 表示表单加载时复选框已被勾选,double_opt_in 为 confirmed 或 pending(使用验证链接时会自动确认)。只需提供有变化的部分;GET /consent 可读回。覆盖欧盟和欧洲经济区、英国、美国、加拿大、澳大利亚、新西兰、瑞士、日本、韩国、新加坡和香港,每个判定都注明其官方来源和我们核查的日期。各项规则见各国营销邮件规则。这是测试辅助工具,不是法律建议。

GET /v1/addresses/{address}/checks 对一封邮件运行检查:与计数使用相同过滤条件(since、service、type、subtype)匹配的最新邮件,或 email_id 指定的邮件。它不会等待,所以请先等待邮件。每项检查结果为通过、失败、警告或跳过,并附一行原因;没有失败时 passed 为 true:

检查 失败或警告的条件
spf, dkim, dmarc 发送服务器无权为该域名发信、邮件未签名,或未通过 From 域名的 DMARC 策略:收件箱可能将其归为垃圾邮件或拒收。这些是接收服务器自己的判定
local_links 某个链接直接或经重定向(例如 Supabase 的 redirect_to)指向 localhost 或私有地址。指向标记为开发环境的主机(在 Project settings 中或 POST /v1/project/dev-hosts)的链接会通过
plain_http 公开网站上的链接不是 https
expected_host 使用 expect_host=staging.example.com 时:登录、验证、重置或邀请链接最终跳到了别处,例如生产环境。打开应用的链接(myapp://…)没有网站,不做比较。不提供时跳过
expiry 邮件没有说明其登录验证码或链接何时失效(警告;其他链接如新闻邮件的链接跳过)
coupon_expiry Labs。 邮件含有优惠券,但没有说明何时到期,例如“有效期至 10 月 31 日”或“7 天内有效”(警告)。邮件中的每张优惠券都计入,而不只是主要验证码。没有优惠券时跳过
plain_text 没有纯文本版本(警告)
size HTML 超过 102 KB,Gmail 会在此处截断(警告)
images_only 几乎全是图片,关闭图片后几乎没有内容
layout 可能不适合手机显示:有固定宽度超过 640 px 且没有移动端样式的部分(@media)、没有 alt 文本的图片(关闭图片后为空白),或小于 11 px 的文字。这是警告,仅根据 HTML 判断,不进行渲染
expected_text 使用 expect_text 时(可重复,最多 10 个短语):主题或正文缺少其中某个,例如用户的名字或产品名称。大小写、弯引号和空格不影响,原因只列出缺少的短语。不提供时跳过
expected_attachment 使用 expect_attachment=.pdf 时(可重复以指定更多):其中某个未附上。不提供时,或对于在记录附件之前到达的邮件,跳过
arrival_time 使用 since 和 expect_within=5m(或 90s、2h,秒数)时:邮件在 since 之后超过该时间才到达,例如花了 14 分钟的欢迎优惠券。不提供时跳过
list_unsubscribe Labs。 仅限营销邮件:没有 List-Unsubscribe 头(失败),或只有电子邮件地址或普通 http 地址(警告)。有了它,收件箱会显示自己的退订按钮,Gmail 和 Yahoo 要求批量发件人提供它
one_click_unsubscribe Labs。 仅限营销邮件:没有带 https 地址的 List-Unsubscribe-Post: List-Unsubscribe=One-Click 头,或已验证的 DKIM 签名未覆盖两个 List-Unsubscribe 头(RFC 8058 要求覆盖,否则收件箱不提供一键退订),因此收件箱的按钮无法一步退订(警告)
unsubscribe_link Labs。 仅限营销邮件:邮件本身没有退订或邮件偏好链接
after_unsubscribe Labs。 仅限营销邮件:它在该地址退订其发件人之后到达(见退订监视)。地址未标记为已退订该发件人时跳过
offer_reuse Labs。 某发件人的优惠券,而该发件人已向该地址的另一种写法(name+promo1@…、大写)发送过优惠券:应用把一个人当成了多个人。没有优惠券的邮件跳过
coupon_result Labs。 记录结果后,结账对其优惠券的处理:在到期日之后、对重复使用的优惠或一次性优惠的第二次使用仍被接受(失败);出错,或在有效期内首次使用被拒绝(警告)。没有结果时跳过
marketing_consent Labs。 向拒绝营销的账号发送营销邮件,而当地需要事先同意(欧盟和欧洲经济区、英国、加拿大、澳大利亚、新西兰、瑞士、日本、韩国)。在美国,或未记录同意时跳过
prechecked_consent Labs。 同意来自预先勾选的复选框(欧盟和欧洲经济区、英国)
double_opt_in Labs。 在地址确认订阅之前发送营销邮件:在德国为警告,在项目要求双重确认时为失败
unsubscribe_deadline Labs。 退订后仍发送营销邮件:在允许的时间内为警告(美国、加拿大、新加坡和香港 10 个工作日,澳大利亚和新西兰 5 个,欧盟、英国、瑞士、日本和韩国须及时),超过后为失败
postal_address Labs。 营销邮件中未找到邮政地址(美国、加拿大;警告)
sender_identity Labs。 From 行没有写明发件人(警告)
minor_marketing Labs。 向英国未满 13 岁或欧盟和欧洲经济区未满 16 岁的角色发送营销邮件(警告)
misleading_subject Labs。 美国、加拿大、香港:主题开头像回复或转发(Re:、Fwd:),或声称优惠今天结束而其优惠券实际还能用好几天(警告;华盛顿州和加利福尼亚州的法律适用于美国角色)
ad_label Labs。 新加坡:向拒绝营销的账号发送营销邮件,且主题不以 <ADV> 开头(警告)
harmful_attachment Labs。 附件是 Gmail 会拦截 的文件类型(.exe、.js、.iso 等),或是伪装成文档的程序(.pdf.exe):失败。可带宏的 Office 文件、HTML 或 SVG 页面以及压缩包会警告。原因只列出扩展名,从不列出文件。没有附件时跳过
spam_risk Labs。 满分 10 的垃圾邮件评分(score),累加垃圾邮件过滤器考量的特征(markers,每项附分值):从 5 分起失败,此时收件箱很可能拒收或将其归为垃圾邮件;从 1.5 分起警告。硬性拦截项计 5 或 6 分:DMARC 在拒收或隔离策略下失败、SPF 和 DKIM 均未通过、被拦截的附件。其次 2 到 3 分:没有 DMARC 策略、没有 DKIM、几乎全是图片、指向裸 IP 地址的链接、短链接、没有 List-Unsubscribe 头的营销邮件。再次最多 1.5 分:SPF 失败、有风险的附件、未签名的一键退订、以 Re: 开头的营销主题、全大写或带 !!! 的主题、典型垃圾邮件用语、显示另一个网站的链接文字、没有纯文本。这是估算:收件箱还会考量发件人信誉,而这只有它们能看到
preheader Labs。 仅限营销邮件:HTML 开头没有隐藏的预览文本,因此收件箱会显示其开头文字,常常是“在浏览器中查看”(警告)
language 使用 expect_language=de(或 pt-BR)时:邮件是另一种语言。文字太少无法判断时警告。支持英语、德语、法语、西班牙语、意大利语、荷兰语、葡萄牙语、瑞典语、波兰语、土耳其语、芬兰语、俄语、乌克兰语、日语、中文、韩语、阿拉伯语、希伯来语、希腊语、泰语和印地语。不提供时跳过

标记为 Labs 的检查会被略过,直到密钥创建者开启其 Labs 功能:优惠券和营销检查需要 Marketing and offer emails,harmful_attachment 和 spam_risk 需要 Spam score and attachment checks。

curl "https://api.proxymail.qa/v1/addresses/k7f2x9ab12cd@mailclub.dev/checks?type=link&expect_host=staging.example.com" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"
{
"address": "k7f2x9ab12cd@mailclub.dev",
"email": { "id": "…", "received_at": "2026-10-17T10:00:01Z", "service": "Supabase", "service_domain": "supabase.io", "type": "link", "subtype": "verify" },
"passed": false,
"failed": ["local_links"],
"warnings": ["expiry"],
"checks": [
{ "check": "spf", "status": "pass", "detail": "The sending server is allowed to send for supabase.io." },
{ "check": "local_links", "status": "fail", "detail": "A link goes to localhost:3000, which only opens on the computer running that server." }
]
}

登录邮件和收据会跳过四项营销检查,在引入类型之前的邮件也一样。检查最新邮件时,现在可能落到一封新闻邮件上并导致 list_unsubscribe 失败:传入 type、subtype 或 category 来检查你想要的那封邮件。

对于本地化邮件,请检查每个语言区域的版本:?type=code&expect_language=fr&expect_text=Bonjour%20Maya。

响应从不包含邮件内容或其链接:原因最多列出一个域名、链接的主机或你自己的短语,因此智能体无需读取不可信的邮件文本即可据此行动。Node.js 客户端 提供 checkEmail,Python 辅助工具 提供 check_email。

让测试账号保持在已知状态,每个测试借用一个:给账号打上状态标签(verified、premium、trial),然后签出一个。你会拿到一个符合所有筛选条件、且没有被别人签出的账号,最久未使用的优先,保留 minutes(默认 10,最多 60)。并行的测试不会拿到同一个账号。

curl -s -X POST https://api.proxymail.qa/v1/addresses/checkout -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \
-H "Content-Type: application/json" -d '{"tags":["verified","premium"],"country":"DE","minutes":5}'
# { "address": "…", "persona": {…}, "tags": ["premium", "verified"], "checkout": { "token": "…", "until": "…" } }

用它登录(对能读取已保存密码的密钥,可以用它的已保存密码),运行测试,然后用令牌归还。归还时可以记录测试结束后账号的状态,让账号池保持准确:

curl -s -X POST "https://api.proxymail.qa/v1/addresses/$ADDRESS/return" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \
-H "Content-Type: application/json" -d "{\"token\":\"$TOKEN\",\"add_tags\":[\"cancelled\"],\"remove_tags\":[\"premium\"]}"

Node.js 客户端 和 Python 辅助工具 也可以在测试的钩子里创建、检查和重置账号,让测试拿到所需状态的账号。

没有归还的签出会在到期后自动释放,所以崩溃的测试不会长时间占着账号;extend 可以延长时间。所有匹配的账号都被签出时,返回 409,并说明第一个何时空出;没有匹配时返回 404。应用里会显示谁在用某个账号、用到什么时候,项目里的任何人都可以释放它。

一次运行把一个 CI 作业或一个智能体的会话归为一组,并保存其调用所见内容的证据。先开始一次运行,然后在每次调用的 Proxy-Mail-QA-Run 请求头里发送它的 id:

RUN=$(curl -s -X POST https://api.proxymail.qa/v1/runs -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" \
-H "Content-Type: application/json" -d '{"name":"Sign-up tests","source":"ci"}' | jq -r .id)
curl -s "https://api.proxymail.qa/v1/addresses/$ADDRESS/code?since=$SUBMITTED" \
-H "Authorization: Bearer $PROXYMAIL_QA_API_KEY" -H "Proxy-Mail-QA-Run: $RUN"
curl -s -X POST "https://api.proxymail.qa/v1/runs/$RUN/finish" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"
curl -s "https://api.proxymail.qa/v1/runs/$RUN?format=markdown" -H "Authorization: Bearer $PROXYMAIL_QA_API_KEY"

GET /v1/runs/{id} 还会把这次运行和同名的最近 10 次已完成运行做比较(JSON 中的 comparison,即 Markdown 报告顶部附近的列表):邮件明显变慢、邮件数量不同、新出现和时好时坏的测试失败、新失败的检查,以及新的发件人或链接站点。检查过邮件时,deliverability 会说明有多少封可能被拒收或进垃圾邮件(likely_spam)、可能进垃圾邮件(may_spam)或带有收件箱会拦截的附件,每封邮件按最近一次检查只计一次;Markdown 报告在标题下的一行里说明同样的内容。这是根据每封邮件的 spam_risk 检查得出的估计,所以需要在 Labs 中打开垃圾邮件评分和附件检查。

每次调用都会加入它所见的内容:创建的地址(包括表单值)或从池中签出的地址、到达的邮件(验证码,或链接的站点和路径,其中看起来像令牌的部分会隐藏,以及在 since 之后多少秒到达)、计数、邮件检查、退订、优惠和优惠券检查,以及没有到达或不该到达却到达的邮件。结束时,除非其中有项失败、某条备注判定失败或你发送 {"failed": true},否则运行通过。报告是 Markdown,可用于拉取请求评论或作业摘要;运行在应用中对项目里的每个人可见,保留 90 天。

要说明某次调用属于哪个测试,在 Proxy-Mail-QA-Test 中发送经过百分号编码的测试名(例如 sign-up%20%E2%80%BA%20sends%20one%20code):报告会把它显示在该调用所见内容旁边。测试运行器的报告器把结果发送到 POST /v1/runs/{id}/tests:每个失败或不稳定的测试作为 {"name", "status": "failed", "duration_ms", "error"}(失败的测试会让运行失败),然后发送 {"totals": {"passed", "failed", "skipped", "flaky"}} 作为报告顶部内容。

证据永远不会让调用失败:如果某次调用无法加入(运行已结束,或不属于此密钥的项目),调用照常工作,Proxy-Mail-QA-Run-Warning 请求头会说明原因。等待结果为空时会重试的客户端,除最后一次外都发送 Proxy-Mail-QA-Run-Retry: true,这样只有最后一次“没有收到”才会计入。在 GitHub Actions 中,Proxy Mail QA action 会完成以上所有事情。

404 也会尽我们所知说明原因:hint 列出我们为该地址拒收的内容(太大或超过限制),或在从未有邮件到达时给出最可能的原因;refused 列出自 since 以来我们拒收的内容;blocked 列出在到达我们之前被 Cloudflare 的邮件服务器拒收的邮件(因 SPF、DKIM 或 DMARC 未通过而被拒、被丢弃或未投递),附带发件人域名及其 SPF、DKIM 和 DMARC 结果;help 链接到 测试邮件收不到?。MCP 工具对 found: false 返回同样的 message、refused 和 help。要随时检查某个地址而不等待,调用 GET /v1/addresses/{address}/delivery:它返回目前收到的邮件、仍有记录的每次拒收,以及 hint。你的应用必须能给任意地址发邮件:处于沙盒或测试模式的邮件服务商只会发往你验证过的地址。

错误以 {"error": "not_found", "message": "…"} 返回,并带有对应的状态码:400、401、402(组织没有套餐)、403(没有权限,或 Labs 功能已关闭)、404、409(none 检查期间有邮件到达)或 429。请求限制见 限制与安全。500,或所有人的请求都超时,可能是我们这边的故障:请看 我们的状态页。

对本页有疑问?

可以用任何语言提问。文档可以立即回答,你也可以把问题发给 Proxy Mail QA 团队,他们会用英文回复。