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.
Cycle de vie
Section intitulée « Cycle de vie »| 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. |
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"Créer une campagne
Section intitulée « Créer une campagne »POST /broadcasts crée un brouillon et renvoie { "id": "..." }.
audience_idstring (uuid)requisL'audience à laquelle envoyer.
fromstringrequisExpéditeur sur un domaine vérifié.
subjectstringrequisDe 1 à 998 caractères. Les placeholders sont pris en charge.
htmlstringfacultatifCorps HTML. Fournissez html, text ou les deux avant l'envoi.
textstringfacultatifCorps en texte brut.
reply_tostring | string[]facultatifJusqu'à 50 adresses.
preview_textstringfacultatifJusqu'à 200 caractères. Affiché après l'objet dans les boîtes de réception ; inséré sous forme de preheader masqué.
namestringfacultatifJusqu'à 100 caractères. Un libellé interne.
Modifier et supprimer
Section intitulée « Modifier et supprimer »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.
# 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" }); // laterSeuls les brouillons avec un corps html ou text peuvent être envoyés, depuis un domaine vérifié. La réponse est { "id": "..." }.
Vérifications de préparation
Section intitulée « Vérifications de préparation »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.
Ce que reçoit un destinataire
Section intitulée « Ce que reçoit un destinataire »- La personnalisation est appliquée à
subject,htmlettext(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êtesList-UnsubscribeetList-Unsubscribe-Post: List-Unsubscribe=One-Clicksont 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.
Quota et limites
Section intitulée « Quota et limites »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.