Kampagnen
Eine Kampagne sendet eine Nachricht an jeden angemeldeten Kontakt einer Zielgruppe. SpacePost fächert sie in eine personalisierte E-Mail pro Kontakt auf, fügt Abmeldelinks und die List-Unsubscribe-Header hinzu, überspringt gesperrte Adressen und erfasst alles wie bei jeder anderen E-Mail.
Lebenszyklus
Abschnitt betitelt „Lebenszyklus“| Status | Bedeutung |
|---|---|
draft |
Erstellt, bearbeitbar, nicht gesendet. |
queued |
send wurde aufgerufen; wartet auf den Startzeitpunkt (sofort oder scheduled_at). |
sending |
Wird an die Kontakte aufgefächert. |
sent |
Jede E-Mail wurde zur Zustellung angenommen. sent_at ist gesetzt. |
failed |
Das Auffächern wurde gestoppt, zum Beispiel weil ein Kontingent erreicht oder das Team pausiert wurde. |
canceled |
Vor dem Senden storniert. |
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);const audienceId = "0192e1c4-0000-7000-8000-0000000000a1";
// 1. Create a draftconst { data: draft, error } = await spacepost.broadcasts.create({ audienceId, name: "October newsletter", from: "Acme <news@acme.com>", replyTo: "support@acme.com", subject: "What's new at Acme, {{{FIRST_NAME|friend}}}", previewText: "Three things we shipped this month", html: ` <h1>Hi {{{FIRST_NAME|there}}},</h1> <p>Here is what is new.</p> <p><a href="{{{SPACEPOST_UNSUBSCRIBE_URL}}}">Unsubscribe</a></p> `,});if (error) throw new Error(error.message);
// 2. Send it now, or schedule itconst { error: sendError } = await spacepost.broadcasts.send(draft.id, { scheduledAt: "in 1 hour" });if (sendError) throw new Error(sendError.message);
// 3. Check progressconst { data: broadcast } = await spacepost.broadcasts.get(draft.id);console.log(broadcast?.status); // "queued" | "sending" | "sent" | "failed"Kampagne erstellen
Abschnitt betitelt „Kampagne erstellen“POST /broadcasts erstellt einen Entwurf und gibt { "id": "..." } zurück.
audience_idstring (uuid)erforderlichDie Zielgruppe, an die gesendet wird.
fromstringerforderlichAbsender auf einer verifizierten Domain.
subjectstringerforderlich1 bis 998 Zeichen. Platzhalter werden unterstützt.
htmlstringoptionalHTML-Body. Gib vor dem Senden html, text oder beides an.
textstringoptionalNur-Text-Body.
reply_tostring | string[]optionalBis zu 50 Adressen.
preview_textstringoptionalBis zu 200 Zeichen. Wird im Posteingang nach dem Betreff angezeigt; als verborgener Preheader eingefügt.
namestringoptionalBis zu 100 Zeichen. Eine interne Bezeichnung.
Aktualisieren und löschen
Abschnitt betitelt „Aktualisieren und löschen“PATCH /broadcasts/{id} akzeptiert dieselben Felder (alle optional) und funktioniert nur bei Entwürfen. DELETE /broadcasts/{id} funktioniert bei Entwürfen und bei Kampagnen, die für die Zukunft geplant sind.
# nowcurl -X POST https://api.spacepost.dev/broadcasts/$ID/send -H "Authorization: Bearer $SPACEPOST_API_KEY"
# scheduled (ISO or natural language, up to 30 days ahead)curl -X POST https://api.spacepost.dev/broadcasts/$ID/send \ -H "Authorization: Bearer $SPACEPOST_API_KEY" -H "Content-Type: application/json" \ -d '{ "scheduled_at": "2026-11-01T09:00:00Z" }'await spacepost.broadcasts.send(id); // nowawait spacepost.broadcasts.send(id, { scheduledAt: "in 1 hour" }); // laterNur Entwürfe mit einem html- oder text-Body können gesendet werden, und zwar von einer verifizierten Domain. Die Antwort lautet { "id": "..." }.
Bereitschaftsprüfungen
Abschnitt betitelt „Bereitschaftsprüfungen“Vor dem Senden liefert GET /broadcasts/{id}/readiness eine Checkliste zur Zustellbarkeit (das Dashboard zeigt dieselbe Liste im Editor). Das ist eine SpacePost-Erweiterung und nicht Teil der Resend-API.
{ "ready": true, "checks": [{ "id": "html_size", "level": "warn", "title": "Gmail will clip this message", "detail": "..." }] }| Prüfung | Stufe | Wann |
|---|---|---|
sender_domain |
error | Die from-Domain fehlt oder ist nicht verifiziert. |
dmarc |
warn | Kein v=DMARC1-Eintrag unter _dmarc.<domain>, oder die Abfrage ist fehlgeschlagen. p=none besteht. |
subject |
error / warn | Error, wenn leer. Warnung bei GROSSBUCHSTABEN, !!! oder $$$ oder mehr als 90 Zeichen. |
html_size |
warn | HTML über 102 KB, das Gmail abschneidet. |
images |
warn | Nur Bilder, oder vier oder mehr Bilder mit wenig Text. |
shorteners |
warn | Links über bit.ly, tinyurl.com, t.co und Ähnliche. |
spam_phrases |
warn | Typische Spam-Formulierungen im Betreff oder Body. |
unsubscribe |
ok | Eine Abmelde-Fußzeile wird automatisch hinzugefügt, wenn dein Body keinen Platzhalter enthält, daher blockiert diese Prüfung nie. |
audience |
error | Keine angemeldeten Kontakte. |
role_addresses |
warn | Mehr als 20 % der Kontakte sind Adressen wie info@ oder admin@. |
ready ist false, wenn eine Prüfung die Stufe error hat. Das Senden schlägt dann mit einem validation_error (422) fehl, der auflistet, was zu beheben ist. Warnungen blockieren das Senden nie.
E-Mails aus Kampagnen tragen außerdem die Header List-Id und Feedback-ID (<broadcast id>:<team id>:broadcast:spacepost), damit die Gmail Postmaster Tools Beschwerden einer Kampagne zuordnen können. Transaktions-E-Mails tragen sie nicht.
Was ein Empfänger erhält
Abschnitt betitelt „Was ein Empfänger erhält“- Personalisierung wird auf
subject,htmlundtextangewendet (Personalisierung). - Abmeldung: Enthält dein Body keinen Platzhalter
{{{SPACEPOST_UNSUBSCRIBE_URL}}}/{{{RESEND_UNSUBSCRIBE_URL}}}, wird automatisch eine Abmelde-Fußzeile angehängt. Die HeaderList-UnsubscribeundList-Unsubscribe-Post: List-Unsubscribe=One-Clickwerden immer hinzugefügt. - Der Vorschautext wird zu einem verborgenen Preheader am Anfang des HTML.
- Abgemeldete Kontakte und Adressen auf deiner Sperrliste werden übersprungen.
Kontingent und Limits
Abschnitt betitelt „Kontingent und Limits“Jeder Empfänger zählt als eine E-Mail auf das Kontingent deines Plans, und für neue Konten gilt die Sandbox mit 100 E-Mails pro Tag. Ist das Kontingent mitten im Versand erschöpft, endet die Kampagne als failed; bereits erstellte E-Mails werden trotzdem zugestellt. Das Auffächern kann fortgesetzt werden, wenn ein Worker neu startet.
Jede E-Mail einer Kampagne ist eine normale E-Mail: Sie erscheint in GET /emails, trägt broadcast_id in Webhook-Payloads und kann als Bounce zurückkommen oder geöffnet werden.