Aller au contenu

Campagnes

Une campagne envoie un message à chaque contact abonné d’une audience. SpacePost la répartit en un e-mail personnalisé par contact, ajoute les liens de désabonnement et les en-têtes List-Unsubscribe, ignore les adresses supprimées et suit tout comme n’importe quel autre e-mail.

Statut Signification
draft Créée, modifiable, non envoyée.
queued send a été appelé ; en attente de son heure de départ (immédiate, ou scheduled_at).
sending En cours de répartition vers les contacts.
sent Chaque e-mail a été accepté pour distribution. sent_at est défini.
failed La répartition s’est arrêtée, par exemple parce qu’un quota a été atteint ou que l’équipe a été mise en pause.
canceled Annulée avant l’envoi.
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 crée un brouillon et renvoie { "id": "..." }.

audience_idstring (uuid)requis

L'audience à laquelle envoyer.

fromstringrequis

Expéditeur sur un domaine vérifié.

subjectstringrequis

De 1 à 998 caractères. Les placeholders sont pris en charge.

htmlstringfacultatif

Corps HTML. Fournissez html, text ou les deux avant l'envoi.

textstringfacultatif

Corps en texte brut.

reply_tostring | string[]facultatif

Jusqu'à 50 adresses.

preview_textstringfacultatif

Jusqu'à 200 caractères. Affiché après l'objet dans les boîtes de réception ; inséré sous forme de preheader masqué.

namestringfacultatif

Jusqu'à 100 caractères. Un libellé interne.

PATCH /broadcasts/{id} accepte les mêmes champs (tous facultatifs) et ne fonctionne que sur les brouillons. DELETE /broadcasts/{id} fonctionne sur les brouillons et sur les campagnes planifiées dans le futur.

Fenêtre de terminal
# 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" }'

Seuls les brouillons avec un corps html ou text peuvent être envoyés, depuis un domaine vérifié. La réponse est { "id": "..." }.

Avant d’envoyer, GET /broadcasts/{id}/readiness renvoie une liste de contrôle de délivrabilité (le tableau de bord affiche la même liste dans l’éditeur). C’est une extension de SpacePost qui ne fait pas partie de l’API de Resend.

{ "ready": true, "checks": [{ "id": "html_size", "level": "warn", "title": "Gmail will clip this message", "detail": "..." }] }
Vérification Niveau Quand
sender_domain error Le domaine from est absent ou n’est pas vérifié.
dmarc warn Aucun enregistrement v=DMARC1 à _dmarc.<domain>, ou la recherche a échoué. p=none passe.
subject error / warn Erreur si vide. Avertissement pour du TOUT EN MAJUSCULES, !!! ou $$$, ou plus de 90 caractères.
html_size warn HTML de plus de 102 Ko, que Gmail tronque.
images warn Uniquement des images, ou quatre images ou plus avec peu de texte.
shorteners warn Liens passant par bit.ly, tinyurl.com, t.co et similaires.
spam_phrases warn Formulations typiques du spam dans l’objet ou le corps.
unsubscribe ok Un pied de page de désabonnement est ajouté automatiquement lorsque votre corps n’a pas de placeholder, donc cela ne bloque jamais.
audience error Aucun contact abonné.
role_addresses warn Plus de 20 % des contacts sont des adresses comme info@ ou admin@.

ready vaut false dès qu’une vérification est au niveau error. L’envoi échoue alors avec une validation_error (422) qui liste ce qu’il faut corriger. Les avertissements ne bloquent jamais un envoi.

Les e-mails de campagne portent aussi les en-têtes List-Id et Feedback-ID (<broadcast id>:<team id>:broadcast:spacepost) afin que Gmail Postmaster Tools puisse attribuer les plaintes à une campagne. Les e-mails transactionnels n’en ont pas.

  • La personnalisation est appliquée à subject, html et text (Personnalisation).
  • Désabonnement : si votre corps n’a pas de placeholder {{{SPACEPOST_UNSUBSCRIBE_URL}}} / {{{RESEND_UNSUBSCRIBE_URL}}}, un pied de page de désabonnement est ajouté automatiquement. Les en-têtes List-Unsubscribe et List-Unsubscribe-Post: List-Unsubscribe=One-Click sont toujours ajoutés.
  • Le Texte d’aperçu devient un preheader masqué en haut du HTML.
  • Les contacts désabonnés et les adresses de votre liste de suppression sont ignorés.

Chaque destinataire compte pour un e-mail dans le quota de votre forfait, et la sandbox de 100 e-mails par jour s’applique aux nouveaux comptes. Si le quota est épuisé en cours d’envoi, la campagne se termine en failed ; les e-mails déjà créés sont tout de même distribués. La répartition peut reprendre si un worker redémarre.

Chaque e-mail de campagne est un e-mail normal : il apparaît dans GET /emails, porte broadcast_id dans les payloads de webhook et peut rebondir ou être ouvert.