用 curl 发送事务邮件(2026):直接调用 Rovela HTTP API
Rovela Email API 的 curl 参考:POST /emails、Idempotency-Key、错误形态、GET 状态、POST /emails/batch、scheduled_at 与取消。
用 curl 发送事务邮件
curl 是不打开 IDE 也能看清 Rovela Email API 的最快办法:一次 POST 到 https://api.rovela.dev/emails,带上 Bearer Key 和 JSON 正文。本文是命令行参考——请求头、幂等、错误、读取状态、批量、定时与取消——写给想在终端里脚本化或排障的人。
它比5 分钟发送第一封邮件(上手路径)更细,也不是在 Node.js 中发送邮件那样的语言教程。HTTP 契约与HTTP 邮件 API相同。可以从Email API产品页、快速开始、发送邮件 API和身份认证入手。
本系列相关产品指南:
- 事务邮件 API
- HTTP 邮件 API
- 从 SMTP 迁到 Email API
- SaaS 应用的 Email API
- 事务邮件 API + 托管邮箱
- 入站与出站 Email API
- 邮件发送 API
- 邮件通知 API
- Email API 集成
- 批量事务邮件 API
- 事务邮件 Webhook
- 简单的事务邮件 API
- 从应用发送事务邮件
- 已验证域名的 Email API
前置条件
- 已验证的发信域名。未验证的
from会以400拒绝(快速开始,已验证域名的 Email API)。 - 形如
re_…的 API Key(在 Dashboard 创建,只展示一次)。仅发送的命令用sending_access;GET、改期、取消需要full_access(身份认证)。 - 支持 TLS 的 curl 7.x+。把 Key 放进环境变量,尽量不要写进 shell 历史:
export ROVELA_API_KEY='re_your_sending_key' # 不要提交
export MAIL_FROM='Acme <hello@yourdomain.com>'
最基本的发送
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer re_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@yourdomain.com>",
"to": "you@example.com",
"subject": "Hello from Rovela",
"html": "<p>It worked.</p>",
"text": "It worked."
}'
成功时得到 {"object":"email","id":"…"}。这表示已入队,不是已投递。能同时给 html 和 text 就同时给——不少客户端会用 text 做预览。
幂等、标签和验证码类邮件
凡是可能重跑的(脚本、CI、不稳定的网络)都应该带 Idempotency-Key(最长 256 字符,24 小时窗口):
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer re_your_sending_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: verify-signup-8f3a2c" \
-d '{
"from": "Acme <no-reply@yourdomain.com>",
"to": "alice@example.com",
"subject": "Your Acme code: 482913",
"html": "<p>Your verification code is</p><p style=\"font-size:28px;letter-spacing:6px\"><b>482913</b></p><p>It expires in 10 minutes.</p>",
"text": "Your verification code is 482913. It expires in 10 minutes."
}'
同一 key 的重试会返回原响应,而不会发出第二个验证码(邮件 API 的幂等键,验证码示例)。
查看投递(需要 full_access)
# 当前状态
curl https://api.rovela.dev/emails/<email_id> -H "Authorization: Bearer re_your_full_access_key"
# 时间线:accepted、delivered、opened、clicked、bounced、failed 等
curl https://api.rovela.dev/emails/<email_id>/events -H "Authorization: Bearer re_your_full_access_key"
这里若用 sending_access 会得到 401。系统化监控请用 webhook,而不是用 curl 轮询(事务邮件 Webhook)。
批量与定时
最多 100 封个性化事务邮件:
curl -X POST https://api.rovela.dev/emails/batch \
-H "Authorization: Bearer re_your_sending_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reminders-2026-11-10" \
-d '[
{
"from": "Acme <reminders@mail.acme.com>",
"to": "alice@example.com",
"subject": "Your trial ends in 3 days",
"html": "<p>Hi Alice, your trial ends on Nov 13.</p>",
"text": "Hi Alice, your trial ends on Nov 13."
},
{
"from": "Acme <reminders@mail.acme.com>",
"to": "bob@example.com",
"subject": "Your trial ends in 3 days",
"html": "<p>Hi Bob, your trial ends on Nov 13.</p>",
"text": "Hi Bob, your trial ends on Nov 13."
}
]'
文档中的批量示例写明:在写入或扣费之前会先检查整单——一条坏记录(域名未验证、正文为空)会让整批以 400 被拒绝。受理之后各条独立投递;批量里被抑制的地址会被跳过(状态 suppressed),而不会让整单失败。batch 端点会拒绝 template 和 topic_id。见批量与定时发送和批量事务邮件 API。
用将来的 ISO 8601 scheduled_at 定时,必须带时区偏移:
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer re_your_sending_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@mail.acme.com>",
"to": "alice@example.com",
"subject": "How is your first week going?",
"html": "<p>Hi Alice, any questions so far? Just reply to this email.</p>",
"text": "Hi Alice, any questions so far? Just reply to this email.",
"scheduled_at": "2026-11-17T09:00:00+08:00"
}'
消息仍为 scheduled 时可改期或取消(若已入队但尚未交给邮件服务器,取消同样有效)。两者都需要 full_access:
curl -X PATCH https://api.rovela.dev/emails/<email_id> -H "Authorization: Bearer re_your_full_access_key" -H "Content-Type: application/json" -d '{"scheduled_at": "2026-11-18T09:00:00+08:00"}'
curl -X POST https://api.rovela.dev/emails/<email_id>/cancel -H "Authorization: Bearer re_your_full_access_key"
# {"object": "email", "id": "<email_id>", "cancelled": true}
一旦已经在投递路上,两者都会返回 400。更多背景见定时发送邮件。
实际会碰到的错误
错误形态类似 {"statusCode":400,"name":"Bad Request","message":"html or text body is required"}。日志里请原样保留 message(快速开始)。
| 状态码 | 常见原因 |
|---|---|
400 | 缺正文、附件 base64 无法解码、from 域名不属于你的组织、模板不存在,或收件人超过 50 |
401 | Key 缺失/无效,或用 sending_access 去读信、改期、取消 |
429 | 触发限流。批量里每条单独计数。若有 Retry-After 请遵守 |
FAQ
能用文件作为正文吗? 可以——--data-binary @payload.json 并设置 Content-Type: application/json。Key 放在头里(-H),不要放进会提交的文件。
curl 需要特殊的 Rovela 参数吗? 不需要。就是普通 HTTPS(Email API)。
如何附上 PDF? 在 attachments 数组里加入带 filename、content_type 和 base64 content 的项。在 CLI 里拼这个比较别扭;完整例子见发票附件和发送带 PDF 附件的发票邮件。
CI 里能用 curl 给团队发信吗? 可以,只要任务跑在服务端且 Key 是机密。按「某次流水线 + 用途」使用唯一的 Idempotency-Key,避免重试重复发送。
把 curl 留在工具箱里
生产出问题时,把同一次 POST 缩成一条 curl(换一个一次性收件人,并有意识地处理幂等键)仍然是最快的复现方式。应用代码里,请把同一套契约包起来,见从应用发送事务邮件。