E-Mail senden
POST https://api.spacepost.dev/emails
Sende eine E-Mail an bis zu 50 to-Empfänger, optional mit cc, bcc, Anhängen, Tags, Headern und einem Zeitplan. Der Aufruf kehrt zurück, sobald die E-Mail angenommen und eingereiht ist; die Zustellung erfolgt asynchron und wird über Webhooks gemeldet.
Beispiel
Abschnitt betitelt „Beispiel“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" }Request-Body
Abschnitt betitelt „Request-Body“fromstringerforderlichAbsender als email@domain.com oder Name <email@domain.com>. Die Domain (oder eine übergeordnete Domain) muss verifiziert sein. Die einzige Ausnahme ist onboarding@test.spacepost.dev, siehe Testen.
tostring | string[]erforderlichEmpfänger. Bis zu 50 Adressen.
subjectstringerforderlich1 bis 998 Zeichen.
htmlstringoptionalHTML-Body. Mindestens eines von html oder text ist erforderlich.
textstringoptionalNur-Text-Body. Wenn du nur html sendest, wird eine Textalternative für dich erzeugt, was die Zustellbarkeit verbessert.
ccstring | string[]optionalBis zu 50 Adressen.
bccstring | string[]optionalBis zu 50 Adressen. Wird nie in die Nachrichten-Header geschrieben.
reply_tostring | string[]optionalBis zu 50 Adressen. Der camelCase-Alias replyTo wird ebenfalls akzeptiert.
scheduled_atstringoptionalISO-8601-Datum oder natürliche Sprache wie in 1 hour, höchstens 30 Tage im Voraus. Alias scheduledAt.
headersobjectoptionalEigene Header als String-Schlüssel und String-Werte.
attachmentsobject[]optionalBis zu 50 Anhänge, jeder mit filename und genau einem von content oder path.
tagsobject[]optionalBis zu 50 { name, value }-Paare. Namen und Werte: 1 bis 256 Zeichen aus A-Z a-z 0-9 _ -.
tenant_idstringoptionalPlattform-Modus: im Namen eines Tenants senden (unsere ID oder deine external_id). Optional, wenn die from-Domain zu einem Tenant gehört. Alias tenantId im Node SDK. Siehe Plattform-Modus.
Mehr dazu: Domain-Verifizierung, Testen, Zeitplanung, Eigene Header, Anhänge, Tags, Plattform-Modus.
Adressen in jedem Feld dürfen die Form Name <email@domain.com> verwenden.
Limits auf einen Blick
Abschnitt betitelt „Limits auf einen Blick“| Limit | Wert |
|---|---|
Empfänger pro Feld (to, cc, bcc, reply_to) |
50 |
| Länge des Betreffs | 998 Zeichen |
| Anhänge | 50 pro E-Mail, filename bis zu 255 Zeichen |
| Tags | 50 pro E-Mail, Name und Wert jeweils 1 bis 256 Zeichen aus A-Z a-z 0-9 _ - |
| Gesamtgröße der Nachricht | 40 MB einschließlich Anhängen und Kodierungs-Overhead |
| Zeitplanungs-Horizont | 30 Tage |
| E-Mails pro Batch-Anfrage | 100 (Batch-Versand) |
Antwort
Abschnitt betitelt „Antwort“Bei Erfolg wird 200 mit der neuen E-Mail-id (einer UUIDv7) zurückgegeben. Nutze sie mit GET /emails/{id}, um die E-Mail und ihr last_event abzurufen.
Was schiefgehen kann
Abschnitt betitelt „Was schiefgehen kann“| Status | name |
Bedeutung |
|---|---|---|
| 401 | missing_api_key |
Kein Authorization: Bearer-Header. |
| 403 | invalid_api_key |
Der Schlüssel ist unbekannt, widerrufen oder kein SpacePost-Schlüssel (sp_live_...). |
| 401 | restricted_api_key |
Ein auf eine Domain beschränkter Schlüssel wurde mit einer anderen from-Domain verwendet. |
| 403 | invalid_from_address |
Die from-Domain ist für dein Team nicht verifiziert. |
| 403 | validation_error |
test.spacepost.dev wurde als Absender verwendet, mit einem Empfänger, der weder Teammitglied noch Testadresse ist. |
| 403 | sending_paused |
Dein Team ist pausiert. |
| 422 | validation_error |
Ein Feld fehlt oder ist ungültig. message nennt das Feld, zum Beispiel `to`: Invalid email address. |
| 429 | daily_quota_exceeded |
Tageslimit erreicht: die Sandbox für neue Konten, dein Limit nach Vertrauensstufe oder das daily_limit eines Tenants. |
| 429 | monthly_quota_exceeded |
Das Monatslimit des Free-Plans ist erreicht. |
| 429 | rate_limit_exceeded |
Mehr Anfragen pro Sekunde als dein Limit erlaubt. Siehe Rate Limits. |
Die vollständige Liste findest du unter Fehler.
Nach dem Aufruf
Abschnitt betitelt „Nach dem Aufruf“Die E-Mail durchläuft Status, die als last_event bereitgestellt werden: queued (oder scheduled), sent, dann delivered, delivery_delayed, bounced, complained, opened oder clicked. Steht jeder Empfänger auf deiner Sperrliste, wird nichts gesendet und der Status wird suppressed.