send-email by resend/resend-skills
npx skills add https://github.com/resend/resend-skills --skill send-emailResend 提供两个用于发送邮件的端点:
| 方式 | 端点 | 使用场景 |
|---|---|---|
| 单封邮件 | POST /emails | 单笔事务性邮件、带附件的邮件、定时发送 |
| 批量邮件 | POST /emails/batch | 单次请求发送多封不同的邮件(最多 100 封),批量通知 |
选择批量发送的场景:
选择单封发送的场景:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
在生产环境发送邮件时,务必实施这些措施。完整实现请参见 references/best-practices.md。
在重试失败的请求时防止重复发送邮件。
关键信息 |
---|---
格式(单封) | <事件类型>/<实体ID>(例如:welcome-email/user-123)
格式(批量) | batch-<事件类型>/<批次ID>(例如:batch-orders/batch-456)
过期时间 | 24 小时
最大长度 | 256 个字符
重复负载 | 返回原始响应,不重新发送
不同负载 | 返回 409 错误
| 代码 | 操作 |
|---|---|
| 400, 422 | 修复请求参数,不要重试 |
| 401, 403 | 检查 API 密钥 / 验证域名,不要重试 |
| 409 | 幂等性冲突 - 使用新密钥或修复负载 |
| 429 | 速率限制 - 使用指数退避重试(默认速率限制为每秒 2 个请求) |
| 500 | 服务器错误 - 使用指数退避重试 |
端点: POST /emails(优先使用 SDK 而非 cURL)
| 参数 | 类型 | 描述 |
|---|---|---|
from | string | 发件人地址。格式:"名称 <email@domain.com>" |
to | string[] | 收件人地址(最多 50 个) |
subject | string | 邮件主题行 |
html 或 text | string | 邮件正文内容 |
| 参数 | 类型 | 描述 |
|---|---|---|
cc | string[] | 抄送收件人 |
bcc | string[] | 密送收件人 |
reply_to* | string[] | 回复地址 |
scheduled_at* | string | 定时发送时间(ISO 8601 格式) |
attachments | array | 文件附件(总计最大 40MB) |
tags | array | 用于跟踪的键/值对(参见标签部分) |
headers | object | 自定义标头 |
*参数命名因 SDK 而异(例如,Node.js 中为 replyTo,Python 中为 reply_to)。
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
const { data, error } = await resend.emails.send(
{
from: 'Acme <onboarding@resend.dev>',
to: ['delivered@resend.dev'],
subject: 'Hello World',
html: '<p>Email body here</p>',
},
{ idempotencyKey: `welcome-email/${userId}` }
);
if (error) {
console.error('Failed:', error.message);
return;
}
console.log('Sent:', data.id);
所有 SDK 实现(包含错误处理和重试逻辑)请参见 references/single-email-examples.md。
端点: POST /emails/batch(但优先使用 SDK 而非 cURL)
由于任何验证错误都会导致整个批次失败,请在发送前验证所有邮件:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
const { data, error } = await resend.batch.send(
[
{
from: 'Acme <notifications@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Order Shipped',
html: '<p>Your order has shipped!</p>',
},
{
from: 'Acme <notifications@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Order Confirmed',
html: '<p>Your order is confirmed!</p>',
},
],
{ idempotencyKey: `batch-orders/${batchId}` }
);
if (error) {
console.error('Batch failed:', error.message);
return;
}
console.log('Sent:', data.map(e => e.id));
所有 SDK 实现(包含验证、错误处理和重试逻辑)请参见 references/batch-email-examples.md。
对于超过 100 封邮件的发送,请将其分块为多个批次请求:
<批次前缀>/chunk-<索引>完整的分块实现请参见 references/batch-email-examples.md。
遵循这些实践以最大化收件箱到达率。
有关送达率的更多帮助,请使用 npx skills add resend/email-best-practices 安装 email-best-practices 技能。
| 实践 | 原因 |
|---|---|
| 有效的 SPF、DKIM、DMARC 记录 | 验证邮件并防止伪造 |
| 链接匹配发送域名 | 如果从 @acme.com 发送,链接到 https://acme.com - 不匹配的域名会触发垃圾邮件过滤器 |
| 包含纯文本版本 | 同时使用 html 和 text 参数以提高可访问性和送达率(如果未提供,Resend 会生成纯文本版本) |
| 避免使用 "no-reply" 地址 | 使用真实地址(例如 support@)- 改善信任信号 |
| 保持正文小于 102KB | Gmail 会截断更大的消息 |
| 实践 | 原因 |
|---|---|
| 使用子域名 | 从 notifications.acme.com 发送事务性邮件,从 mail.acme.com 发送营销邮件 - 保护声誉 |
| 对事务性邮件禁用跟踪 | 打开/点击跟踪可能触发密码重置、收据等邮件的垃圾邮件过滤器 |
跟踪在 Resend 控制台的域名级别配置,而非每封邮件。
| 设置 | 工作原理 | 建议 |
|---|---|---|
| 打开跟踪 | 插入 1x1 透明像素 | 对事务性邮件禁用 - 可能损害送达率 |
| 点击跟踪 | 通过重写链接进行重定向 | 对敏感邮件禁用(密码重置、安全警报) |
何时启用跟踪:
何时禁用跟踪:
通过控制台配置:域名 → 配置 → 点击/打开跟踪
使用 webhooks 实时跟踪邮件递送状态。当事件发生时,Resend 会向你的端点发送 HTTP POST 请求。
| 事件 | 使用时机 |
|---|---|
email.delivered | 确认成功递送 |
email.bounced | 从邮件列表中移除,提醒用户 |
email.complained | 退订用户(垃圾邮件投诉) |
email.opened / email.clicked | 跟踪参与度(仅限营销) |
验证每个请求的 webhook 签名。 如果不验证,任何人都可以向你的端点发送虚假事件。
设置、签名验证代码和所有事件类型请参见 references/webhooks.md。
标签是帮助你跟踪和筛选邮件的键/值对。
tags: [
{ name: 'user_id', value: 'usr_123' },
{ name: 'email_type', value: 'welcome' },
{ name: 'plan', value: 'enterprise' }
]
使用场景:
约束: 标签名称和值只能包含 ASCII 字母、数字、下划线或破折号。每个最多 256 个字符。
使用预构建的模板,而不是每次请求都发送 HTML。
const { data, error } = await resend.emails.send({
from: 'Acme <hello@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Welcome!',
template: {
id: 'tmpl_abc123', // 或别名:'welcome-email'
variables: {
USER_NAME: 'John', // 区分大小写!必须与模板完全匹配。
ORDER_TOTAL: '$99.00'
}
}
});
注意: 变量名称区分大小写,必须与模板定义完全匹配。任何大小写都有效 - USER_NAME、userName 或 user_name - 只要发送调用使用的与模板中定义的相同。
| 事实 | 详情 |
|---|---|
| 保留名称 | FIRST_NAME、LAST_NAME、EMAIL、UNSUBSCRIBE_URL、contact、this |
| 回退值 | 可选 - 如果未设置且在发送时缺少变量,发送会失败(422) |
| 不能与以下参数结合使用 | html、text 或 react 参数 |
| subject / from | 模板默认值可以在每次发送时被覆盖 |
模板必须发布后才能使用。草稿模板无法发送。
要通过 API 创建、更新、发布或删除模板,请使用 templates 技能。
避免使用真实邮件提供商的虚假地址进行测试。
使用诸如 test@gmail.com、example@outlook.com 或 fake@yahoo.com 之类的地址会导致:
| 方法 | 地址 | 结果 |
|---|---|---|
| 已送达 | delivered@resend.dev | 模拟成功递送 |
| 已退回 | bounced@resend.dev | 模拟硬退回 |
| 已投诉 | complained@resend.dev | 模拟垃圾邮件投诉 |
| 你自己的邮箱 | 你的实际地址 | 真实递送测试 |
对于开发: 使用 resend.dev 测试地址来模拟不同场景,而不影响你的声誉。
对于预发布环境: 发送到你控制的真实地址(团队成员、你拥有的测试账户)。
新域名必须逐渐增加发送量以建立声誉。
为什么重要: 新域名突然发送大量邮件会触发垃圾邮件过滤器。ISP 期望逐渐增长。
现有域名
| 天数 | 每日邮件数 | 每小时邮件数 |
|---|---|---|
| 1 | 最多 1,000 封邮件 | 最多 100 封 |
| 2 | 最多 2,500 封邮件 | 最多 300 封 |
| 3 | 最多 5,000 封邮件 | 最多 600 封 |
| 4 | 最多 5,000 封邮件 | 最多 800 封 |
| 5 | 最多 7,500 封邮件 | 最多 1,000 封 |
| 6 | 最多 7,500 封邮件 | 最多 1,500 封 |
| 7 | 最多 10,000 封邮件 | 最多 2,000 封 |
新域名
| 天数 | 每日邮件数 | 每小时邮件数 |
|---|---|---|
| 1 | 最多 150 封邮件 | |
| 2 | 最多 250 封邮件 | |
| 3 | 最多 400 封邮件 | |
| 4 | 最多 700 封邮件 | 最多 50 封 |
| 5 | 最多 1,000 封邮件 | 最多 75 封 |
| 6 | 最多 1,500 封邮件 | 最多 100 封 |
| 7 | 最多 2,000 封邮件 | 最多 150 封 |
| 指标 | 目标 | 如果超出应采取的行动 |
|---|---|---|
| 退回率 | < 4% | 放慢速度,清理列表 |
| 垃圾邮件投诉率 | < 0.08% | 放慢速度,审查内容 |
不要使用第三方预热服务。 专注于向真实、积极参与的收件人发送相关内容。
Resend 自动管理一个不应接收邮件的地址抑制列表。
地址在以下情况下被添加:
会发生什么: Resend 不会尝试向被抑制的地址递送邮件。相反,会触发 email.suppressed webhook 事件。
为什么重要: 继续向退回/投诉的地址发送邮件会破坏你的声誉。抑制列表会自动保护你。
管理: 在 Resend 控制台的“抑制”下查看和管理被抑制的地址。
| 错误 | 修复方法 |
|---|---|
| 重试时不使用幂等键 | 始终包含幂等键 - 防止重试时重复发送 |
| 对带附件的邮件使用批量发送 | 批量发送不支持附件 - 改用单封发送 |
| 发送前未验证批次 | 首先验证所有邮件 - 一封无效邮件会导致整个批次失败 |
| 重试 400/422 错误 | 这些是验证错误 - 修复请求,不要重试 |
| 相同的幂等键,不同的负载 | 返回 409 错误 - 每个唯一的邮件内容使用唯一的键 |
| 对事务性邮件启用跟踪 | 对密码重置、收据等禁用打开/点击跟踪 - 损害送达率 |
| 使用 "no-reply" 发件人地址 | 使用真实地址如 support@ - 改善与邮件提供商的信任信号 |
| 不验证 webhook 签名 | 始终验证 - 未验证的事件不可信 |
| 使用虚假邮件测试(test@gmail.com) | 使用 delivered@resend.dev - 虚假地址会退回并损害声誉 |
| 模板变量名称不匹配 | 变量名称区分大小写 - 必须与模板定义完全匹配。任何大小写都有效;USER_NAME 和 firstName 都可以,只要发送调用与之匹配。 |
| 从新域名发送大量邮件 | 逐渐预热 - 突然激增会触发垃圾邮件过滤器 |
from 地址必须使用已验证的域名reply_to 参数设置为有效地址。RESEND_API_KEY 环境变量中react 参数error、data、headers。{ id: "email-id" },或(批量)返回 ID 数组每周安装次数
459
仓库
GitHub 星标数
92
首次出现
Jan 23, 2026
安全审计
安装于
claude-code364
cursor313
opencode274
codex238
gemini-cli234
antigravity222
Resend provides two endpoints for sending emails:
| Approach | Endpoint | Use Case |
|---|---|---|
| Single | POST /emails | Individual transactional emails, emails with attachments, scheduled sends |
| Batch | POST /emails/batch | Multiple distinct emails in one request (max 100), bulk notifications |
Choose batch when:
Choose single when:
Always implement these for production email sending. See references/best-practices.md for complete implementations.
Prevent duplicate emails when retrying failed requests.
Key Facts |
---|---
Format (single) | <event-type>/<entity-id> (e.g., welcome-email/user-123)
Format (batch) | batch-<event-type>/<batch-id> (e.g., batch-orders/batch-456)
Expiration | 24 hours
Max length | 256 characters
Duplicate payload | Returns original response without resending
Different payload | Returns 409 error
| Code | Action |
|---|---|
| 400, 422 | Fix request parameters, don't retry |
| 401, 403 | Check API key / verify domain, don't retry |
| 409 | Idempotency conflict - use new key or fix payload |
| 429 | Rate limited - retry with exponential backoff (by default, rate limit is 2 requests/second) |
| 500 | Server error - retry with exponential backoff |
Endpoint: POST /emails (prefer SDK over cURL)
| Parameter | Type | Description |
|---|---|---|
from | string | Sender address. Format: "Name <email@domain.com>" |
to | string[] | Recipient addresses (max 50) |
subject | string | Email subject line |
html or text | string | Email body content |
| Parameter | Type | Description |
|---|---|---|
cc | string[] | CC recipients |
bcc | string[] | BCC recipients |
reply_to* | string[] | Reply-to addresses |
scheduled_at* | string | Schedule send time (ISO 8601) |
attachments | array | File attachments (max 40MB total) |
*Parameter naming varies by SDK (e.g., replyTo in Node.js, reply_to in Python).
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
const { data, error } = await resend.emails.send(
{
from: 'Acme <onboarding@resend.dev>',
to: ['delivered@resend.dev'],
subject: 'Hello World',
html: '<p>Email body here</p>',
},
{ idempotencyKey: `welcome-email/${userId}` }
);
if (error) {
console.error('Failed:', error.message);
return;
}
console.log('Sent:', data.id);
See references/single-email-examples.md for all SDK implementations with error handling and retry logic.
Endpoint: POST /emails/batch (but prefer SDK over cURL)
Since the entire batch fails on any validation error, validate all emails before sending:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
const { data, error } = await resend.batch.send(
[
{
from: 'Acme <notifications@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Order Shipped',
html: '<p>Your order has shipped!</p>',
},
{
from: 'Acme <notifications@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Order Confirmed',
html: '<p>Your order is confirmed!</p>',
},
],
{ idempotencyKey: `batch-orders/${batchId}` }
);
if (error) {
console.error('Batch failed:', error.message);
return;
}
console.log('Sent:', data.map(e => e.id));
See references/batch-email-examples.md for all SDK implementations with validation, error handling, and retry logic.
For sends larger than 100 emails, chunk into multiple batch requests:
<batch-prefix>/chunk-<index>See references/batch-email-examples.md for complete chunking implementations.
Follow these practices to maximize inbox placement.
For more help with deliverability, install the email-best-practices skill with npx skills add resend/email-best-practices.
| Practice | Why |
|---|---|
| Valid SPF, DKIM, DMARC record | authenticate the email and prevent spoofing |
| Links match sending domain | If sending from @acme.com, link to https://acme.com - mismatched domains trigger spam filters |
| Include plain text version | Use both html and text parameters for accessibility and deliverability (Resend generates a plain text version if not provided) |
| Avoid "no-reply" addresses | Use real addresses (e.g., support@) - improves trust signals |
| Keep body under 102KB |
| Practice | Why |
|---|---|
| Use subdomains | Send transactional from notifications.acme.com, marketing from mail.acme.com - protects reputation |
| Disable tracking for transactional | Open/click tracking can trigger spam filters for password resets, receipts, etc. |
Tracking is configured at the domain level in the Resend dashboard, not per-email.
| Setting | How it works | Recommendation |
|---|---|---|
| Open tracking | Inserts 1x1 transparent pixel | Disable for transactional emails - can hurt deliverability |
| Click tracking | Rewrites links through redirect | Disable for sensitive emails (password resets, security alerts) |
When to enable tracking:
When to disable tracking:
Configure via dashboard: Domain → Configuration → Click/Open Tracking
Track email delivery status in real-time using webhooks. Resend sends HTTP POST requests to your endpoint when events occur.
| Event | When to use |
|---|---|
email.delivered | Confirm successful delivery |
email.bounced | Remove from mailing list, alert user |
email.complained | Unsubscribe user (spam complaint) |
email.opened / email.clicked | Track engagement (marketing only) |
Verify webhook signatures for every request. Without verification, anyone can send fake events to your endpoint.
See references/webhooks.md for setup, signature verification code, and all event types.
Tags are key/value pairs that help you track and filter emails.
tags: [
{ name: 'user_id', value: 'usr_123' },
{ name: 'email_type', value: 'welcome' },
{ name: 'plan', value: 'enterprise' }
]
Use cases:
Constraints: Tag names and values can only contain ASCII letters, numbers, underscores, or dashes. Max 256 characters each.
Use pre-built templates instead of sending HTML with each request.
const { data, error } = await resend.emails.send({
from: 'Acme <hello@acme.com>',
to: ['delivered@resend.dev'],
subject: 'Welcome!',
template: {
id: 'tmpl_abc123', // or alias: 'welcome-email'
variables: {
USER_NAME: 'John', // Case-sensitive! Must match template exactly.
ORDER_TOTAL: '$99.00'
}
}
});
Note: Variable names are case-sensitive and must match the template definition exactly. Any casing is valid — USER_NAME, userName, or user_name — as long as the send call uses the same casing as defined in the template.
| Fact | Detail |
|---|---|
| Reserved names | FIRST_NAME, LAST_NAME, EMAIL, UNSUBSCRIBE_URL, contact, this |
| Fallback values | Optional — if not set and variable missing at send time, send fails (422) |
| Can't combine with | html, text, or parameters |
Templates must be published before use. Draft templates cannot send.
To create, update, publish, or delete templates via API, use the templates skill.
Avoid testing with fake addresses at real email providers.
Using addresses like test@gmail.com, example@outlook.com, or fake@yahoo.com will:
| Method | Address | Result |
|---|---|---|
| Delivered | delivered@resend.dev | Simulates successful delivery |
| Bounced | bounced@resend.dev | Simulates hard bounce |
| Complained | complained@resend.dev | Simulates spam complaint |
| Your own email | Your actual address | Real delivery test |
For development: Use the resend.dev test addresses to simulate different scenarios without affecting your reputation.
For staging: Send to real addresses you control (team members, test accounts you own).
New domains must gradually increase sending volume to establish reputation.
Why it matters: Sudden high volume from a new domain triggers spam filters. ISPs expect gradual growth.
Existing domain
| Day | Messages per day | Messages per hour |
|---|---|---|
| 1 | Up to 1,000 emails | 100 Maximum |
| 2 | Up to 2,500 emails | 300 Maximum |
| 3 | Up to 5,000 emails | 600 Maximum |
| 4 | Up to 5,000 emails | 800 Maximum |
| 5 | Up to 7,500 emails | 1,000 Maximum |
| 6 | Up to 7,500 emails | 1,500 Maximum |
| 7 | Up to 10,000 emails | 2,000 Maximum |
New domain
| Day | Messages per day | Messages per hour |
|---|---|---|
| 1 | Up to 150 emails | |
| 2 | Up to 250 emails | |
| 3 | Up to 400 emails | |
| 4 | Up to 700 emails | 50 Maximum |
| 5 | Up to 1,000 emails | 75 Maximum |
| 6 | Up to 1,500 emails | 100 Maximum |
| 7 | Up to 2,000 emails | 150 Maximum |
| Metric | Target | Action if exceeded |
|---|---|---|
| Bounce rate | < 4% | Slow down, clean list |
| Spam complaint rate | < 0.08% | Slow down, review content |
Don't use third-party warm-up services. Focus on sending relevant content to real, engaged recipients.
Resend automatically manages a suppression list of addresses that should not receive emails.
Addresses are added when:
What happens: Resend won't attempt delivery to suppressed addresses. The email.suppressed webhook event fires instead.
Why this matters: Continuing to send to bounced/complained addresses destroys your reputation. The suppression list protects you automatically.
Management: View and manage suppressed addresses in the Resend dashboard under Suppressions.
| Mistake | Fix |
|---|---|
| Retrying without idempotency key | Always include idempotency key - prevents duplicate sends on retry |
| Using batch for emails with attachments | Batch doesn't support attachments - use single sends instead |
| Not validating batch before send | Validate all emails first - one invalid email fails the entire batch |
| Retrying 400/422 errors | These are validation errors - fix the request, don't retry |
| Same idempotency key, different payload | Returns 409 error - use unique key per unique email content |
| Tracking enabled for transactional emails | Disable open/click tracking for password resets, receipts - hurts deliverability |
| Using "no-reply" sender address | Use real address like support@ - improves trust signals with email providers |
| Not verifying webhook signatures | Always verify — unverified events can't be trusted |
| Testing with fake emails (test@gmail.com) | Use - fake addresses bounce and hurt reputation |
from address must use a verified domainreply_to parameter to a valid address.RESEND_API_KEY environment variablereact parameter for React Email componentserror, data, headers in the response.{ id: "email-id" } on success (single) or array of IDs (batch)Weekly Installs
459
Repository
GitHub Stars
92
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code364
cursor313
opencode274
codex238
gemini-cli234
antigravity222
Azure Data Explorer (Kusto) 查询技能:KQL数据分析、日志遥测与时间序列处理
100,500 周安装
Rust调用关系图生成器 - 可视化函数调用层次结构,提升代码分析效率
539 周安装
parallel-web-extract:并行网页内容提取工具,高效抓取网页数据
595 周安装
腾讯云CloudBase AI模型Web技能:前端调用混元/DeepSeek模型,实现流式文本生成
560 周安装
Apollo Connectors 模式助手:GraphQL API 连接器开发与集成指南
565 周安装
GitHub Trending 趋势分析工具:实时发现热门项目、技术洞察与开源机会
556 周安装
GSAP React 集成教程:useGSAP Hook 动画库与 React 组件开发指南
546 周安装
tags | array | Key/value pairs for tracking (see Tags) |
headers | object | Custom headers |
| Gmail clips larger messages |
react| subject / from | Template defaults can be overridden per-send |
delivered@resend.dev| Template variable name mismatch | Variable names are case-sensitive — must match the template definition exactly. Any casing is valid; USER_NAME and firstName are both fine as long as the send call matches. |
| Sending high volume from new domain | Warm up gradually - sudden spikes trigger spam filters |