Zum Inhalt springen

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.

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.
broadcast.ts
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
const audienceId = "0192e1c4-0000-7000-8000-0000000000a1";
// 1. Create a draft
const { 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 it
const { error: sendError } = await spacepost.broadcasts.send(draft.id, { scheduledAt: "in 1 hour" });
if (sendError) throw new Error(sendError.message);
// 3. Check progress
const { data: broadcast } = await spacepost.broadcasts.get(draft.id);
console.log(broadcast?.status); // "queued" | "sending" | "sent" | "failed"

POST /broadcasts erstellt einen Entwurf und gibt { "id": "..." } zurück.

audience_idstring (uuid)erforderlich

Die Zielgruppe, an die gesendet wird.

fromstringerforderlich

Absender auf einer verifizierten Domain.

subjectstringerforderlich

1 bis 998 Zeichen. Platzhalter werden unterstützt.

htmlstringoptional

HTML-Body. Gib vor dem Senden html, text oder beides an.

textstringoptional

Nur-Text-Body.

reply_tostring | string[]optional

Bis zu 50 Adressen.

preview_textstringoptional

Bis zu 200 Zeichen. Wird im Posteingang nach dem Betreff angezeigt; als verborgener Preheader eingefügt.

namestringoptional

Bis zu 100 Zeichen. Eine interne Bezeichnung.

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.

Terminal-Fenster
# now
curl -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" }'

Nur Entwürfe mit einem html- oder text-Body können gesendet werden, und zwar von einer verifizierten Domain. Die Antwort lautet { "id": "..." }.

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.

  • Personalisierung wird auf subject, html und text angewendet (Personalisierung).
  • Abmeldung: Enthält dein Body keinen Platzhalter {{{SPACEPOST_UNSUBSCRIBE_URL}}} / {{{RESEND_UNSUBSCRIBE_URL}}}, wird automatisch eine Abmelde-Fußzeile angehängt. Die Header List-Unsubscribe und List-Unsubscribe-Post: List-Unsubscribe=One-Click werden immer hinzugefügt.
  • Der Vorschautext wird zu einem verborgenen Preheader am Anfang des HTML.
  • Abgemeldete Kontakte und Adressen auf deiner Sperrliste werden übersprungen.

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.