이메일 보내기
POST https://api.spacepost.dev/emails
최대 50명의 to 수신자에게 이메일 한 통을 보냅니다. cc, bcc, 첨부 파일, 태그, 헤더, 예약 발송은 선택 사항입니다. 호출은 이메일이 접수되어 큐에 들어가는 즉시 반환되며, 전달은 비동기로 이루어지고 결과는 웹훅으로 알려 줍니다.
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개 주소. camelCase 별칭 replyTo도 허용됩니다.
scheduled_atstring선택ISO 8601 날짜 또는 in 1 hour 같은 자연어. 최대 30일 이후까지. 별칭은 scheduledAt.
headersobject선택문자열 키와 문자열 값으로 이루어진 사용자 지정 헤더.
attachmentsobject[]선택최대 50개의 첨부 파일. 각각 filename과 content 또는 path 중 정확히 하나를 가집니다.
tagsobject[]선택최대 50개의 { name, value } 쌍. 이름과 값은 A-Z a-z 0-9 _ - 문자로 1자 이상 256자 이하.
tenant_idstring선택플랫폼 모드: 테넌트를 대신해 발송합니다(SpacePost ID 또는 사용자의 external_id). from 도메인이 테넌트에 속해 있으면 생략할 수 있습니다. Node SDK에서는 별칭 tenantId를 사용합니다. 플랫폼 모드를 참고하세요.
더 보기: 도메인 확인, 테스트, 예약 발송, 사용자 지정 헤더, 첨부 파일, 태그, 플랫폼 모드.
모든 필드의 주소에 Name <email@domain.com> 형식을 사용할 수 있습니다.
한도 요약
섹션 제목: “한도 요약”| 항목 | 값 |
|---|---|
필드별 수신자 수(to, cc, bcc, reply_to) |
50 |
| 제목 길이 | 998자 |
| 첨부 파일 | 이메일당 50개, filename은 255자까지 |
| 태그 | 이메일당 50개, 이름과 값은 A-Z a-z 0-9 _ - 문자로 1자 이상 256자 이하 |
| 메시지 전체 크기 | 첨부 파일과 인코딩 오버헤드를 포함해 40 MB |
| 예약 가능 기간 | 30일 |
| 배치 요청당 이메일 수 | 100(배치 발송) |
성공하면 새 이메일의 id(UUIDv7)와 함께 200이 반환됩니다. 이 ID로 GET /emails/{id}를 호출하면 이메일과 last_event를 확인할 수 있습니다.
발생할 수 있는 오류
섹션 제목: “발생할 수 있는 오류”| 상태 | 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가 됩니다.