Inviare un'email
POST https://api.spacepost.dev/emails
Invia un’email a un massimo di 50 destinatari to, con cc, bcc, allegati, tag, header e pianificazione facoltativi. La chiamata restituisce la risposta non appena l’email viene accettata e messa in coda; la consegna avviene in modo asincrono e viene segnalata dai webhook.
Esempio
Sezione intitolata “Esempio”import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
const { data, error } = await spacepost.emails.send({ from: "Acme <hello@acme.com>", to: ["user@example.com"], cc: ["manager@example.com"], bcc: ["archive@acme.com"], replyTo: "support@acme.com", subject: "Your invoice", html: "<p>Invoice attached.</p>", text: "Invoice attached.", headers: { "X-Entity-Ref-ID": "inv_1042" }, tags: [{ name: "category", value: "invoice" }],});
if (error) { console.error(error.statusCode, error.name, error.message);} else { console.log("Email id:", data.id);}curl -X POST https://api.spacepost.dev/emails \ -H "Authorization: Bearer $SPACEPOST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "Acme <hello@acme.com>", "to": ["user@example.com"], "cc": ["manager@example.com"], "bcc": ["archive@acme.com"], "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "text": "Invoice attached.", "headers": { "X-Entity-Ref-ID": "inv_1042" }, "tags": [{ "name": "category", "value": "invoice" }] }'import os, requests
r = requests.post( "https://api.spacepost.dev/emails", headers={"Authorization": f"Bearer {os.environ['SPACEPOST_API_KEY']}"}, json={ "from": "Acme <hello@acme.com>", "to": ["user@example.com"], "cc": ["manager@example.com"], "bcc": ["archive@acme.com"], "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "text": "Invoice attached.", "headers": {"X-Entity-Ref-ID": "inv_1042"}, "tags": [{"name": "category", "value": "invoice"}], },)print(r.json())payload := map[string]any{ "from": "Acme <hello@acme.com>", "to": []string{"user@example.com"}, "cc": []string{"manager@example.com"}, "reply_to": "support@acme.com", "subject": "Your invoice", "html": "<p>Invoice attached.</p>", "tags": []map[string]string{{"name": "category", "value": "invoice"}},}b, _ := json.Marshal(payload)req, _ := http.NewRequest("POST", "https://api.spacepost.dev/emails", bytes.NewReader(b))req.Header.Set("Authorization", "Bearer "+os.Getenv("SPACEPOST_API_KEY"))req.Header.Set("Content-Type", "application/json")res, err := http.DefaultClient.Do(req)$ch = curl_init('https://api.spacepost.dev/emails');curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SPACEPOST_API_KEY'), 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'from' => 'Acme <hello@acme.com>', 'to' => ['user@example.com'], 'reply_to' => 'support@acme.com', 'subject' => 'Your invoice', 'html' => '<p>Invoice attached.</p>', 'tags' => [['name' => 'category', 'value' => 'invoice']], ]),]);echo curl_exec($ch);require "net/http"require "json"
uri = URI("https://api.spacepost.dev/emails")req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{ENV["SPACEPOST_API_KEY"]}", "Content-Type" => "application/json")req.body = { from: "Acme <hello@acme.com>", to: ["user@example.com"], reply_to: "support@acme.com", subject: "Your invoice", html: "<p>Invoice attached.</p>", tags: [{ name: "category", value: "invoice" }]}.to_jsonres = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }puts res.body{ "id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10" }Corpo della richiesta
Sezione intitolata “Corpo della richiesta”fromstringobbligatorioMittente, come email@domain.com o Name <email@domain.com>. Il dominio (o un dominio padre) deve essere verificato. L'unica eccezione è onboarding@test.spacepost.dev, vedi Test.
tostring | string[]obbligatorioDestinatari. Fino a 50 indirizzi.
subjectstringobbligatorioDa 1 a 998 caratteri.
htmlstringopzionaleCorpo HTML. È richiesto almeno uno tra html e text.
textstringopzionaleCorpo in solo testo. Se invii solo html, viene generata per te un'alternativa testuale, che migliora il recapito.
ccstring | string[]opzionaleFino a 50 indirizzi.
bccstring | string[]opzionaleFino a 50 indirizzi. Mai scritti negli header del messaggio.
reply_tostring | string[]opzionaleFino a 50 indirizzi. È accettato anche l'alias camelCase replyTo.
scheduled_atstringopzionaleData ISO 8601 o linguaggio naturale come in 1 hour, al massimo 30 giorni nel futuro. Alias scheduledAt.
headersobjectopzionaleHeader personalizzati come chiavi stringa e valori stringa.
attachmentsobject[]opzionaleFino a 50 allegati, ciascuno con filename ed esattamente uno tra content e path.
tagsobject[]opzionaleFino a 50 coppie { name, value }. Nomi e valori: da 1 a 256 caratteri tra A-Z a-z 0-9 _ -.
tenant_idstringopzionaleModalità piattaforma: invia per conto di un tenant (il nostro id o il tuo external_id). Facoltativo quando il dominio di from appartiene a un tenant. Alias tenantId nell'SDK Node. Vedi Modalità piattaforma.
Altro: verifica del dominio, Test, Pianificazione, Header personalizzati, Allegati, Tag, Modalità piattaforma.
Gli indirizzi di ogni campo possono usare la forma Name <email@domain.com>.
Limiti in sintesi
Sezione intitolata “Limiti in sintesi”| Limite | Valore |
|---|---|
Destinatari per campo (to, cc, bcc, reply_to) |
50 |
| Lunghezza dell’oggetto | 998 caratteri |
| Allegati | 50 per email, filename fino a 255 caratteri |
| Tag | 50 per email, nome e valore da 1 a 256 caratteri tra A-Z a-z 0-9 _ - |
| Dimensione totale del messaggio | 40 MB inclusi allegati e overhead di codifica |
| Orizzonte di pianificazione | 30 giorni |
| Email per richiesta batch | 100 (Invio in batch) |
Risposta
Sezione intitolata “Risposta”In caso di successo viene restituito 200 con l’id della nuova email (un UUIDv7). Usalo con GET /emails/{id} per leggere l’email e il suo last_event.
Che cosa può andare storto
Sezione intitolata “Che cosa può andare storto”| Stato | name |
Significato |
|---|---|---|
| 401 | missing_api_key |
Manca l’header Authorization: Bearer. |
| 403 | invalid_api_key |
La chiave è sconosciuta, revocata o non è una chiave SpacePost (sp_live_...). |
| 401 | restricted_api_key |
Una chiave limitata a un dominio è stata usata con un dominio from diverso. |
| 403 | invalid_from_address |
Il dominio di from non è verificato per il tuo team. |
| 403 | validation_error |
Uso di test.spacepost.dev come mittente con un destinatario che non è un membro del team né un indirizzo di test. |
| 403 | sending_paused |
Il tuo team è in pausa. |
| 422 | validation_error |
Un campo manca o non è valido. message indica il campo, ad esempio `to`: Invalid email address. |
| 429 | daily_quota_exceeded |
Raggiunto il limite giornaliero: la sandbox dei nuovi account, il tuo tetto per livello di fiducia o il daily_limit di un tenant. |
| 429 | monthly_quota_exceeded |
Raggiunto il limite mensile del piano Free. |
| 429 | rate_limit_exceeded |
Superato il tuo limite di richieste al secondo. Vedi Limiti di frequenza. |
Trovi l’elenco completo in Errori.
Dopo la chiamata
Sezione intitolata “Dopo la chiamata”L’email passa attraverso diversi stati, esposti come last_event: queued (o scheduled), sent, poi delivered, delivery_delayed, bounced, complained, opened o clicked. Se tutti i destinatari sono nella tua lista di soppressione, non viene inviato nulla e lo stato diventa suppressed.