Campagne
Una campagna invia un messaggio a ogni contatto iscritto di un pubblico. SpacePost lo distribuisce in una email personalizzata per ogni contatto, aggiunge i link di annullamento dell’iscrizione e gli header List-Unsubscribe, salta gli indirizzi soppressi e traccia tutto come qualsiasi altra email.
Ciclo di vita
Sezione intitolata “Ciclo di vita”| Stato | Significato |
|---|---|
draft |
Creata, modificabile, non inviata. |
queued |
È stato chiamato send; in attesa dell’orario di avvio (subito, oppure scheduled_at). |
sending |
In corso di distribuzione ai contatti. |
sent |
Ogni email è stata accettata per la consegna. sent_at è impostato. |
failed |
La distribuzione si è fermata, ad esempio perché è stata raggiunta una quota o il team è stato messo in pausa. |
canceled |
Annullata prima dell’invio. |
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"Creare una campagna
Sezione intitolata “Creare una campagna”POST /broadcasts crea una bozza e restituisce { "id": "..." }.
audience_idstring (uuid)obbligatorioIl pubblico a cui inviare.
fromstringobbligatorioMittente su un dominio verificato.
subjectstringobbligatorioDa 1 a 998 caratteri. I segnaposto sono supportati.
htmlstringopzionaleCorpo HTML. Fornisci html, text o entrambi prima dell'invio.
textstringopzionaleCorpo in solo testo.
reply_tostring | string[]opzionaleFino a 50 indirizzi.
preview_textstringopzionaleFino a 200 caratteri. Mostrato dopo l'oggetto nelle caselle di posta; inserito come preheader nascosto.
namestringopzionaleFino a 100 caratteri. Un'etichetta interna.
Aggiornare ed eliminare
Sezione intitolata “Aggiornare ed eliminare”PATCH /broadcasts/{id} accetta gli stessi campi (tutti facoltativi) e funziona solo sulle bozze. DELETE /broadcasts/{id} funziona sulle bozze e sulle campagne pianificate nel futuro.
Inviare
Sezione intitolata “Inviare”# 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" }); // laterPossono essere inviate solo le bozze con un corpo html o text, da un dominio verificato. La risposta è { "id": "..." }.
Controlli di prontezza
Sezione intitolata “Controlli di prontezza”Prima di inviare, GET /broadcasts/{id}/readiness restituisce una checklist di recapito (la dashboard mostra lo stesso elenco nell’editor). È un’estensione di SpacePost e non fa parte dell’API di Resend.
{ "ready": true, "checks": [{ "id": "html_size", "level": "warn", "title": "Gmail will clip this message", "detail": "..." }] }| Controllo | Livello | Quando |
|---|---|---|
sender_domain |
error | Il dominio di from manca o non è verificato. |
dmarc |
warn | Nessun record v=DMARC1 in _dmarc.<domain>, oppure la ricerca è fallita. p=none è accettato. |
subject |
error / warn | Error se è vuoto. Warning per TUTTO MAIUSCOLO, !!! o $$$, oppure per più di 90 caratteri. |
html_size |
warn | HTML oltre i 102 KB, che Gmail tronca. |
images |
warn | Solo immagini, oppure quattro o più immagini con poco testo. |
shorteners |
warn | Link tramite bit.ly, tinyurl.com, t.co e simili. |
spam_phrases |
warn | Formulazioni tipiche dello spam nell’oggetto o nel corpo. |
unsubscribe |
ok | Un piè di pagina di annullamento dell’iscrizione viene aggiunto automaticamente quando il tuo corpo non ha un segnaposto, quindi non blocca mai. |
audience |
error | Nessun contatto iscritto. |
role_addresses |
warn | Più del 20% dei contatti sono indirizzi come info@ o admin@. |
ready è false quando un qualsiasi controllo ha livello error. L’invio fallisce quindi con un validation_error (422) che elenca che cosa correggere. I warning non bloccano mai un invio.
Le email delle campagne includono anche gli header List-Id e Feedback-ID (<broadcast id>:<team id>:broadcast:spacepost) in modo che Gmail Postmaster Tools possa attribuire i reclami a una campagna. Le email transazionali no.
Che cosa riceve un destinatario
Sezione intitolata “Che cosa riceve un destinatario”- La personalizzazione viene applicata a
subject,htmletext(Personalizzazione). - Annullamento dell’iscrizione: se il tuo corpo non ha il segnaposto
{{{SPACEPOST_UNSUBSCRIBE_URL}}}/{{{RESEND_UNSUBSCRIBE_URL}}}, viene aggiunto automaticamente un piè di pagina di annullamento dell’iscrizione. Gli headerList-UnsubscribeeList-Unsubscribe-Post: List-Unsubscribe=One-Clickvengono sempre aggiunti. - Il Testo di anteprima diventa un preheader nascosto in cima all’HTML.
- I contatti che hanno annullato l’iscrizione e gli indirizzi nella tua lista di soppressione vengono saltati.
Quota e limiti
Sezione intitolata “Quota e limiti”Ogni destinatario conta come una email sulla quota del tuo piano, e per i nuovi account vale la sandbox da 100 email al giorno. Se la quota si esaurisce a metà invio, la campagna termina come failed; le email già create vengono comunque consegnate. La distribuzione è riprendibile se un worker si riavvia.
Ogni email di una campagna è una normale email: compare in GET /emails, include broadcast_id nei payload dei webhook e può rimbalzare o essere aperta.