发送邮件
POST https://api.spacepost.dev/emails
向最多 50 个 to 收件人发送一封邮件,可选 cc、bcc、附件、标签、标头和发送计划。邮件一经接受并入队,调用就会返回;投递是异步进行的,结果通过 Webhook 报告。
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
const { data, error } = await spacepost.emails.send({ from: "Acme <hello@acme.com>", to: ["user@example.com"], cc: ["manager@example.com"], bcc: ["archive@acme.com"], replyTo: "support@acme.com", subject: "Your invoice", html: "<p>Invoice attached.</p>", text: "Invoice attached.", headers: { "X-Entity-Ref-ID": "inv_1042" }, tags: [{ name: "category", value: "invoice" }],});
if (error) { console.error(error.statusCode, error.name, error.message);} else { console.log("Email id:", data.id);}curl -X POST https://api.spacepost.dev/emails \ -H "Authorization: Bearer $SPACEPOST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "Acme <hello@acme.com>", "to": ["user@example.com"], "cc": ["manager@example.com"], "bcc": ["archive@acme.com"], "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "text": "Invoice attached.", "headers": { "X-Entity-Ref-ID": "inv_1042" }, "tags": [{ "name": "category", "value": "invoice" }] }'import os, requests
r = requests.post( "https://api.spacepost.dev/emails", headers={"Authorization": f"Bearer {os.environ['SPACEPOST_API_KEY']}"}, json={ "from": "Acme <hello@acme.com>", "to": ["user@example.com"], "cc": ["manager@example.com"], "bcc": ["archive@acme.com"], "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "text": "Invoice attached.", "headers": {"X-Entity-Ref-ID": "inv_1042"}, "tags": [{"name": "category", "value": "invoice"}], },)print(r.json())payload := map[string]any{ "from": "Acme <hello@acme.com>", "to": []string{"user@example.com"}, "cc": []string{"manager@example.com"}, "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "tags": []map[string]string{{"name": "category", "value": "invoice"}},}b, _ := json.Marshal(payload)req, _ := http.NewRequest("POST", "https://api.spacepost.dev/emails", bytes.NewReader(b))req.Header.Set("Authorization", "Bearer "+os.Getenv("SPACEPOST_API_KEY"))req.Header.Set("Content-Type", "application/json")res, err := http.DefaultClient.Do(req)$ch = curl_init('https://api.spacepost.dev/emails');curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SPACEPOST_API_KEY'), 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'from' => 'Acme <hello@acme.com>', 'to' => ['user@example.com'], 'reply_to' => 'support@acme.com', 'subject' => 'Your invoice', 'html' => '<p>Invoice attached.</p>', 'tags' => [['name' => 'category', 'value' => 'invoice']], ]),]);echo curl_exec($ch);require "net/http"require "json"
uri = URI("https://api.spacepost.dev/emails")req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{ENV["SPACEPOST_API_KEY"]}", "Content-Type" => "application/json")req.body = { from: "Acme <hello@acme.com>", to: ["user@example.com"], reply_to: "support@acme.com", subject: "Your invoice", html: "<p>Invoice attached.</p>", tags: [{ name: "category", value: "invoice" }]}.to_jsonres = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }puts res.body{ "id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10" }fromstring必填发件人,格式为 email@domain.com 或 Name <email@domain.com>。该域名(或其父域名)必须已验证。唯一的例外是 onboarding@test.spacepost.dev,参见“测试”。
tostring | string[]必填收件人。最多 50 个地址。
subjectstring必填1 到 998 个字符。
htmlstring可选HTML 正文。html 和 text 至少需要提供一个。
textstring可选纯文本正文。如果只发送 html,系统会为你生成纯文本替代内容,这有助于提高送达率。
ccstring | string[]可选最多 50 个地址。
bccstring | string[]可选最多 50 个地址。绝不会写入邮件标头。
reply_tostring | string[]可选最多 50 个地址。也接受驼峰别名 replyTo。
scheduled_atstring可选ISO 8601 日期,或 in 1 hour 这样的自然语言,最多提前 30 天。别名 scheduledAt。
headersobject可选自定义标头,键和值均为字符串。
attachmentsobject[]可选最多 50 个附件,每个附件包含 filename,以及 content 或 path 中的恰好一个。
tagsobject[]可选最多 50 个 { name, value } 键值对。名称和值:1 到 256 个字符,仅限 A-Z a-z 0-9 _ -。
tenant_idstring可选平台模式:代表某个租户发送(使用我们的 id 或你的 external_id)。当 from 域名属于某个租户时可省略。Node SDK 中的别名为 tenantId。参见“平台模式”。
更多内容:域名验证、测试、定时发送、自定义标头、附件、标签、平台模式。
所有字段中的地址都可以使用 Name <email@domain.com> 的形式。
| 限制 | 值 |
|---|---|
每个字段的收件人数(to、cc、bcc、reply_to) |
50 |
| 主题长度 | 998 个字符 |
| 附件 | 每封邮件 50 个,filename 最长 255 个字符 |
| 标签 | 每封邮件 50 个,名称和值为 1 到 256 个字符,仅限 A-Z a-z 0-9 _ - |
| 邮件总大小 | 40 MB,包括附件和编码开销 |
| 定时发送范围 | 30 天 |
| 每个批量请求的邮件数 | 100(批量发送) |
成功时返回 200 以及新邮件的 id(UUIDv7)。配合 GET /emails/{id} 使用它,即可读取邮件及其 last_event。
可能出现的问题
Section titled “可能出现的问题”| 状态码 | name |
含义 |
|---|---|---|
| 401 | missing_api_key |
缺少 Authorization: Bearer 标头。 |
| 403 | invalid_api_key |
密钥未知、已吊销,或不是 SpacePost 密钥(sp_live_...)。 |
| 401 | restricted_api_key |
使用了限定域名的密钥,但 from 域名与之不同。 |
| 403 | invalid_from_address |
from 域名尚未在你的团队中验证。 |
| 403 | validation_error |
使用 test.spacepost.dev 作为发件人,但收件人既不是团队成员也不是测试地址。 |
| 403 | sending_paused |
你的团队已被暂停。 |
| 422 | validation_error |
字段缺失或无效。message 会指出具体字段,例如 `to`: Invalid email address。 |
| 429 | daily_quota_exceeded |
已达到每日上限:新账户沙盒、你的信任等级上限或租户的 daily_limit。 |
| 429 | monthly_quota_exceeded |
已达到 Free 套餐的每月上限。 |
| 429 | rate_limit_exceeded |
超出了你的每秒请求数限制。参见速率限制。 |
完整列表见错误。
邮件会依次经历多个状态,并通过 last_event 对外呈现:queued(或 scheduled)、sent,然后是 delivered、delivery_delayed、bounced、complained、opened 或 clicked。如果所有收件人都在你的抑制列表中,则不会发送任何内容,状态变为 suppressed。