In questa pagina
Un’importazione può restituire 201 ed essere comunque sbagliata: ID sorgente perso, lista errata o disiscrizione precedente ignorata. Sono scelte sulla proprietà dei dati, non dettagli HTTP.
Questa guida fa funzionare una richiesta Brevo e poi mostra le verifiche necessarie per una piccola sincronizzazione ripetibile.
Cosa copre l’API di Brevo
Tutto vive sotto un unico host e un unico percorso di versione. La documentazione per sviluppatori raggruppa la superficie in quattro aree di prodotto:
- Messaggistica: email transazionali, SMS e WhatsApp, inclusi invii batch, programmazione e attività dei messaggi.
- Piattaforma di marketing: contatti, liste, segmenti e campagne email.
- eCommerce: prodotti, ordini e tracciamento degli eventi cliente.
- Conversations: il widget di chat e la gestione programmatica delle conversazioni.
Queste aree condividono un solo account, un solo database di contatti e una sola chiave API. È comodo e ogni tanto pericoloso: uno script scritto pensando a un ambiente di staging sta parlando con gli stessi contatti a cui inviano le tue campagne.
Transazionale contro marketing
Le due famiglie si comportano in modo abbastanza diverso da rendere la confusione tra loro l’errore di progettazione più comune.
| Transazionale | Marketing | |
|---|---|---|
| Endpoint principale | POST /v3/smtp/email | POST /v3/emailCampaigns |
| Destinatari | Espliciti nella richiesta | listIds o segmentIds |
| Attivazione | La tua applicazione, in tempo reale | Programmata o inviata su richiesta |
| Forma tipica del volume | Continua, un messaggio alla volta | A picchi, un unico invio grande |
| Posizione sui limiti di frequenza | Molto alta, 1.000 richieste al secondo sul livello generale | Bassa, gli endpoint campagna ricadono nel limite generale |
Se stai ancora decidendo se Brevo sia la piattaforma giusta, la panoramica della piattaforma copre quel terreno.
Autenticazione e gestione delle chiavi
Brevo usa una semplice chiave API in un header personalizzato. L’header si chiama api-key, non Authorization, e non ha prefisso Bearer. Questo confonde quasi tutti quelli che hanno usato prima un’altra API di messaggistica.
OAuth può usare un token Bearer per le operazioni supportate; una chiave API standard va invece nell’header api-key.
curl https://api.brevo.com/v3/account \ -H "api-key: $BREVO_API_KEY"Le chiavi si generano nell’app di Brevo, nelle impostazioni dell’account, nella sezione SMTP e API, alla scheda delle chiavi API. Dai a ogni chiave un nome descrittivo legato al sistema che la usa. Il valore della chiave viene mostrato esattamente una volta al momento della generazione, quindi se lo perdi ne generi una nuova invece di recuperare la vecchia.
Qualche regola pratica:
- Emetti una chiave separata per ogni ambiente di deploy e per ogni servizio. Revocare una chiave compromessa non dovrebbe mai mettere fuori uso tre sistemi scollegati.
- Le chiavi API standard valgono per l’intero account. Considera qualsiasi chiave come accesso completo a contatti, invii e dati CRM.
- Brevo supporta anche OAuth 2.0 per le applicazioni che agiscono per conto di altri account Brevo, descritto insieme al flusso a chiave negli schemi di autenticazione.
- Il server MCP usato dagli assistenti AI richiede un token separato e usa davvero un header bearer. Quel token si genera nella stessa schermata delle chiavi API, ma non è intercambiabile con una chiave REST.
Base URL, versionamento e la tua prima scrittura
Il base URL è https://api.brevo.com/v3/. La versione sta nel percorso invece che in un header, e v3 è la generazione attuale. Ogni percorso in questa guida è relativo a quella base.
Per la prima scrittura di un contatto usa un indirizzo di prova sotto il tuo controllo, un ID lista reale e un ID sorgente stabile. Crea prima l’attributo FNAME in Brevo. Con una chiave vera questo esempio scrive nell’account:
curl -X POST https://api.brevo.com/v3/contacts \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "[email protected]", "ext_id": "test-source-contact-1", "attributes": { "FNAME": "Dev" }, "listIds": [42], "updateEnabled": true, "getId": true }'Sostituisci 42 con l’ID della lista di prova. Rileggi il contatto e verifica ID, nome, lista e blocchi. Una risposta 2xx da sola non prova il risultato voluto.
Gli endpoint che userai davvero
Contatti
POST /v3/contacts crea un contatto. Il corpo accetta email, attributes dichiarati, listIds ed ext_id come chiave sorgente stabile. updateEnabled: true aggiorna un contatto esistente e getId: true richiede l’ID risultante. Brevo documenta questi campi. Riusa lo stesso ext_id in ogni esecuzione e conserva la corrispondenza tra ID sorgente e ID Brevo.
Prima di scrivere, decidi quale sistema possiede ciascun attributo e quale lista riceve i contatti consenzienti. Specifica listIds. Non impostare mai emailBlacklisted: false negli aggiornamenti ordinari. Brevo avverte che cambiare l’indirizzo di un contatto bloccato rimuove il blocco. Tratta il cambio separatamente. Controlla blocchi e disiscrizioni prima di aggiungere il contatto a una lista marketing. Rimane una corsa tra lettura e scrittura; per una garanzia rigorosa serve un’ultima verifica prima dell’invio.
Le letture passano da GET /v3/contacts, che pagina con limit (predefinito 50, massimo 1000) e offset, e supporta modifiedSince e createdSince in UTC. Le sincronizzazioni incrementali dovrebbero appoggiarsi a modifiedSince invece di percorrere l’intera lista. Nota che il parametro filter supporta solo un operatore di uguaglianza, quindi qualsiasi cosa più espressiva va in un segmento.
Per il caricamento massivo, POST /v3/contacts/import accetta fileUrl, fileBody o jsonBody, punta a listIds e gira in modo asincrono, restituendo un processId. Brevo documenta un corpo massimo di 10 MB e consiglia di restare vicino agli 8 MB, perché il parsing gonfia il payload. Passa notifyUrl per conoscere l’esito invece di fare polling.
Email transazionali
POST /v3/smtp/email è il cavallo da tiro. Oltre a sender, to, subject e htmlContent, i campi che vale la pena conoscere sono:
templateIdconparams, che sostituisce il contenuto inline con un template di Brevo e le sue variabili. I parametri della singola versione arrivano al massimo a 100 KB, quelli cumulativi a 1000 KB.messageVersions, che invia varianti personalizzate in una sola chiamata, con un massimo di 99 destinatari per versione.tagspossono aiutare a collegare gli eventi di consegna al flusso applicativo che li ha generati.scheduledAtinsieme abatchId, per invii futuri che potresti voler annullare in blocco.headers, in Title-Case, per header SMTP personalizzati.
Una singola richiesta accetta al massimo 2.000 destinatari. Per la differenza tra questo endpoint e l’invio di campagne, la guida alle email transazionali offre la prospettiva di strategia dei messaggi.
Campagne email
POST /v3/emailCampaigns richiede name e sender, più esattamente una sorgente di contenuto: htmlContent (minimo 10 caratteri, sotto 1 MB), htmlUrl oppure templateId. Il pubblico va in recipients come listIds o segmentIds, e scheduledAt usa il formato UTC YYYY-MM-DDTHH:mm:ss.SSSZ. Le rotte complementari coprono l’invio immediato, l’invio di un test, l’aggiornamento dello stato e l’estrazione del report della campagna.
Aziende, trattative e oggetti
Il CRM di Brevo ha due percorsi di scrittura sovrapposti, e scegliere quello giusto conta.
Le rotte CRM sono POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} e l’insieme equivalente per le trattative. Sono sincrone. Una PATCH restituisce 204 una volta applicata la modifica.
L’API objects è il percorso massivo: POST /v3/objects/{object_type}/batch/upsert accetta fino a 1000 record e 1 MB per richiesta, fino a 500 attributi per record e fino a 10 record di associazione per tipo di oggetto per record. Restituisce 202 con un processId, cioè accettato e non applicato.
curl -X POST https://api.brevo.com/v3/objects/company/batch/upsert \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "identifiers": { "id": 12345 }, "attributes": { "domain": "acme.example", "industry": "retail" } } ] }'La guida al CRM di Brevo copre il modello a oggetti dal punto di vista di chi lo usa ogni giorno.
SDK ufficiali
Brevo mantiene i client nell’organizzazione GitHub getbrevo:
| Linguaggio | Repository |
|---|---|
| Node.js | github.com/getbrevo/brevo-node |
| Python | github.com/getbrevo/brevo-python |
| PHP | github.com/getbrevo/brevo-php |
| Java | github.com/getbrevo/brevo-java |
| C# | github.com/getbrevo/brevo-csharp |
| Go | github.com/getbrevo/brevo-go |
| Ruby | github.com/getbrevo/brevo-ruby |
Il client Node si installa come @getbrevo/brevo:
npm install @getbrevo/brevoimport { BrevoClient } from "@getbrevo/brevo";
// Reconcile uncertain send outcomes before retrying.const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY, maxRetries: 0 });
const result = await brevo.transactionalEmails.sendTransacEmail({ subject: "Order confirmed", htmlContent: "<html><body><p>Thanks for your order.</p></body></html>", tags: ["order-confirmation"],});
console.log("Message ID:", result.messageId);Il client Python si installa con pip install brevo-python. Se preferisci non portarti dietro una dipendenza SDK per due endpoint, la superficie HTTP grezza è abbastanza piccola da chiamare direttamente, il che ti tiene anche al riparo dal ricambio di versioni dell’SDK:
Il client Python si installa con pip install brevo-python. Per pochi endpoint puoi chiamare HTTP direttamente:
import osimport requests
BASE = "https://api.brevo.com/v3"HEADERS = {"api-key": os.environ["BREVO_API_KEY"]}
def upsert_consented_contact(source_id, email, first_name, list_id, consent): if not consent: raise ValueError("No marketing consent in the source") if not source_id or not isinstance(list_id, int): raise ValueError("Stable source ID and numeric list ID required") # Configure FNAME in Brevo before using this explicit field mapping. response = requests.post( f"{BASE}/contacts", headers=HEADERS, json={ "email": email, "ext_id": source_id, "attributes": {"FNAME": first_name}, "listIds": [list_id], "updateEnabled": True, "getId": True, }, timeout=30, ) response.raise_for_status() return response.json().get("id") if response.content else NoneUn aggiornamento riuscito può restituire 204 senza corpo. None indica allora di rileggere il contatto per ottenere il suo ID.
L’esempio presume che il chiamante abbia già controllato consenso e blocchi Brevo. Non richiede lo sblocco. Con una chiave reale il codice scrive nell’account; provalo prima con mock. I conflitti di identità possono produrre vari codici 4xx. Leggi la risposta e riconcilia gli ID, senza presumere sempre 409.
Webhook
I webhook sono il modo in cui scopri cosa è successo dopo un invio. POST /v3/webhooks ne crea uno, con url, events, type e facoltativamente channel (email o sms), batched, headers personalizzati e un oggetto auth.
Esistono tre tipi di webhook con vocabolari di eventi distinti:
- Transazionali:
sent,request,delivered,hardBounce,softBounce,blocked,spam,invalid,deferred,click,opened,uniqueOpened,unsubscribed. - Marketing:
spam,opened,click,hardBounce,softBounce,unsubscribed,listAddition,delivered,contactUpdated,contactDeleted. - Inbound:
inboundEmailProcessed, che richiede undomain.
curl -X POST https://api.brevo.com/v3/webhooks \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://yourapp.example/hooks/brevo", "type": "transactional", "events": ["delivered", "hardBounce", "spam", "unsubscribed"], "description": "Deliverability signals" }'Per le modifiche ai contatti registra un webhook marketing con contactUpdated. La documentazione degli eventi chiama il payload contact_updated. Autentica il destinatario con il token bearer auth o gli header personalizzati della guida Brevo. Verifica il segreto prima di elaborare l’evento e non registrarlo nei log.
Salva una chiave di deduplicazione composta da tipo di evento, ID contatto e timestamp per un periodo che copra i tentativi ripetuti. Il solo timestamp non è unico. Le consegne possono ripetersi o arrivare fuori ordine. Dopo un evento rileggi il contatto e riconcilialo con la fonte autorevole. Definisci una finestra di replay, rifiuta eventi troppo vecchi dopo il recupero ed esegui controlli incrementali periodici. Un webhook segnala una modifica, ma non crea da solo una sincronizzazione bidirezionale. La guida ai connettori spiega le alternative.
Limiti di frequenza e gestione degli errori
I limiti di frequenza di Brevo sono per endpoint e per livello di piano, e la distanza tra un endpoint e l’altro è enorme.
| Endpoint | General (tutti i piani) | Advanced (Professional ed Enterprise) |
|---|---|---|
POST /v3/smtp/email | 1.000 RPS | 2.000 RPS |
POST /v3/transactionalSMS/send | 150 RPS | 200 RPS |
/v3/contacts/... | 10 RPS, 36.000 RPH | 20 RPS, 72.000 RPH |
POST /v3/events | 10 RPS, 36.000 RPH | 20 RPS, 72.000 RPH |
GET /v3/smtp/emails | 2 RPS, 7.200 RPH | 3 RPS, 10.800 RPH |
| Endpoint senza un limite proprio | 100 RPH | 200 RPH |
Brevo documenta x-sib-ratelimit-limit, x-sib-ratelimit-remaining e x-sib-ratelimit-reset per gli endpoint limitati. Leggili anche dopo una risposta riuscita. Su 429 attendi il reset e riprova entro un limite fisso di tentativi e tempo. Il valore reset è una durata nell’unità della finestra, di solito secondi, non un timestamp Unix.
async function callBrevo(path, init) { for (let attempt = 0; attempt < 3; attempt++) { const response = await fetch(`https://api.brevo.com/v3${path}`, { ...init, headers: { "api-key": process.env.BREVO_API_KEY, ...init.headers }, }); if (response.status !== 429 || attempt === 2) return response; const raw = Number(response.headers.get("x-sib-ratelimit-reset")); const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt; const delayMs = (seconds + 2 ** attempt) * 1000; if (delayMs > 60_000) return response; await new Promise(resolve => setTimeout(resolve, delayMs)); }}Se l’attesa richiesta supera 60 secondi, la funzione restituisce 429. Pianifica un nuovo tentativo più tardi.
Questo helper va usato solo per richieste che hai verificato essere ripetibili. Dopo un errore di invio ambiguo riconcilia l’ID messaggio prima di reinviare. Registra stato e header dei limiti, mai chiave API o dati dei contatti.
Non riprovare alla cieca dopo un 4xx. La documentazione della creazione contatti elenca 400 e 425; un conflitto di identità non garantisce 409. Dopo un 5xx su una scrittura verifica se la modifica è già stata applicata.
Quale stato è tornato?
-
429
Attendi con un limite
Usa la durata del reset e fermati dopo pochi tentativi.
-
5xx dopo una scrittura
Verifica se è stata applicata
Il server può aver scritto prima dell’errore.
-
400
Correggi la richiesta
Spesso la richiesta è errata.
-
Conflitto di identificatori
Leggi e riconcilia gli ID
L’API non promette un codice di conflitto preciso.
Testare senza inviare email
Aggiungi l’header X-Sib-Sandbox con valore drop a un invio transazionale. Brevo convalida la richiesta, restituisce 201 con un messageId, non consegna nulla e non scrive alcun log email.
curl -X POST https://api.brevo.com/v3/smtp/email \ -H "api-key: $BREVO_API_KEY" \ -H "X-Sib-Sandbox: drop" \ -H "Content-Type: application/json" \ -d '{ "sender": { "email": "[email protected]" }, "to": [{ "email": "[email protected]" }], "subject": "Sandbox", "htmlContent": "<p>hi</p>" }'Capisci bene cosa dimostra e cosa non dimostra. La modalità sandbox convalida solo il formato della richiesta. Non dice nulla sull’autenticazione del mittente, sul rendering dei template o sulla recapitabilità. Tieni un account Brevo separato per i test di integrazione di tutto ciò che tocca contatti o dati CRM, perché la modalità sandbox copre l’invio e non il resto dell’API.
Prima di sincronizzare i contatti
- Scegli un ID sorgente stabile per
ext_ide conserva l’ID Brevo restituito. - Verifica gli attributi prima di scrivere. Brevo può ignorare quelli sconosciuti o del tipo errato; rileggi un contatto di prova.
- Usa una lista esplicita e una prova autorevole del consenso. Salta i contatti bloccati o disiscritti.
- Conta separatamente record accettati, applicati, saltati e falliti. Un 202 per batch o oggetti conferma solo l’accettazione.
- Usa i webhook marketing come segnali e riconcilia lo stato attuale. Autentica, deduplica e limita il replay.
- Controlla gli header dei limiti e fermati dopo pochi tentativi.
Tajo si concentra sulla sincronizzazione dei dati commerciali con Brevo. Se sviluppi questa parte, la guida ai connettori Brevo confronta i percorsi e gli errori.
Domande frequenti
Qual è il base URL dell’API di Brevo?
Tutte le chiamate REST di Brevo passano da https://api.brevo.com/v3/. La versione fa parte del percorso e v3 è la generazione attuale, quindi un endpoint come l’invio transazionale corrisponde al percorso completo https://api.brevo.com/v3/smtp/email.
Come ti autentichi con l’API di Brevo?
Invia la tua chiave in un header HTTP chiamato api-key. Brevo non usa un header Authorization né un token Bearer per le chiavi API standard. Le chiavi si generano nelle impostazioni dell’account, sezione SMTP e API, scheda chiavi API, e il valore viene mostrato una sola volta.
Quali sono i limiti di frequenza dell’API di Brevo?
I limiti sono per endpoint e per livello di limite di frequenza. Sul livello generale, di cui dispone ogni piano, l’endpoint di invio transazionale consente 1.000 richieste al secondo, gli endpoint dei contatti ne consentono 10 al secondo e gli endpoint senza un limite proprio sono fermi a 100 richieste all’ora. I piani Professional ed Enterprise possono usare il livello Advanced, ed Enterprise il livello Extended. Superare un limite restituisce 429.
Brevo ha SDK ufficiali?
Sì. Brevo pubblica client per Node.js, Python, PHP, Java, C#, Go e Ruby nell’organizzazione GitHub getbrevo. Il pacchetto Node è @getbrevo/brevo su npm e quello Python è brevo-python su PyPI.
Come provo l’API di Brevo senza inviare email vere?
Aggiungi l’header X-Sib-Sandbox con valore drop a un invio transazionale. Brevo convalida la richiesta, restituisce 201 con un messageId, non invia nulla e non scrive alcun log email. Controlla solo il formato della richiesta, non la recapitabilità.
Quali eventi webhook supporta Brevo?
I webhook transazionali segnalano anche consegne, rimbalzi e disiscrizioni. I webhook marketing includono contactUpdated e contactDeleted. Per gli eventi in ingresso Brevo documenta inboundEmailProcessed.
Come si tutela una disiscrizione durante un aggiornamento del contatto?
Usa un ID sorgente stabile e attributi dichiarati. Non impostare emailBlacklisted su false nei normali aggiornamenti. Controlla il contatto e la fonte del consenso prima di aggiungerlo a una lista marketing.
Un webhook basta per una sincronizzazione bidirezionale?
No. Autentica il destinatario, elimina gli eventi duplicati e riconcilia quelli persi o fuori ordine con una fonte autorevole. Un webhook segnala un cambiamento, non dimostra che i due sistemi coincidono.