Nesta página
Uma importação de contato pode retornar 201 e ainda estar errada: o ID de origem se perdeu, a lista estava incorreta ou um descadastro anterior foi ignorado. Essas são decisões sobre os dados, não detalhes HTTP.
Este guia mostra uma chamada à Brevo e depois as verificações que tornam uma pequena sincronização repetível.
O que a API da Brevo cobre
Tudo vive sob um único host e um único caminho de versão. A documentação para desenvolvedores agrupa a superfície em quatro áreas de produto:
- Mensagens: e-mail transacional, SMS e WhatsApp, incluindo envios em lote, agendamento e atividade das mensagens.
- Plataforma de marketing: contatos, listas, segmentos e campanhas de e-mail.
- E-commerce: produtos, pedidos e rastreamento de eventos do cliente.
- Conversas: o widget de chat e a gestão programática de conversas.
Essas áreas compartilham uma conta, um banco de contatos e uma chave de API. Isso é conveniente e ocasionalmente perigoso: um script escrito com uma ideia de dados de homologação está falando com os mesmos contatos para os quais suas campanhas enviam.
Transacional versus marketing
As duas famílias se comportam de forma bastante diferente, e confundi-las é o erro de design mais comum.
| Transacional | Marketing | |
|---|---|---|
| Endpoint principal | POST /v3/smtp/email | POST /v3/emailCampaigns |
| Endereçamento | Destinatários explícitos na requisição | listIds ou segmentIds |
| Disparo | Sua aplicação, em tempo real | Agendado ou enviado sob demanda |
| Formato típico de volume | Contínuo, uma mensagem por vez | Em picos, um envio grande |
| Postura de limite de taxa | Muito alta, 1.000 requisições por segundo no nível general | Baixa, os endpoints de campanha caem no limite geral |
Se você ainda está decidindo se a Brevo é a plataforma certa, a visão geral da plataforma cobre esse terreno.
Autenticação e gestão de chaves
A Brevo usa uma chave de API simples em um cabeçalho personalizado. O cabeçalho se chama api-key, não Authorization, e não existe prefixo Bearer. Isso derruba quase todo mundo que já usou outra API de mensagens antes.
O OAuth pode usar um token Bearer nas operações suportadas; uma chave API padrão continua no cabeçalho api-key.
curl https://api.brevo.com/v3/account \ -H "api-key: $BREVO_API_KEY"As chaves são geradas no app da Brevo, nas configurações da conta, na seção SMTP e API, na aba de chaves de API. Dê a cada chave um nome descritivo ligado ao sistema que a usa. O valor da chave aparece exatamente uma vez, no momento em que é gerada, então, se você perdê-lo, gera uma nova chave em vez de recuperar a antiga.
Algumas regras práticas:
- Emita uma chave separada por ambiente de deploy e por serviço. Revogar uma chave comprometida nunca deveria derrubar três sistemas sem relação entre si.
- As chaves de API padrão valem para a conta inteira. Trate qualquer chave como acesso total a contatos, envios e dados de CRM.
- A Brevo também é compatível com OAuth 2.0 para aplicações que agem em nome de outras contas Brevo, descrito junto ao fluxo de chaves em authentication schemes.
- O servidor MCP usado por assistentes de AI usa um token separado e esse sim usa um cabeçalho bearer. Esse token é gerado na mesma tela de chaves de API, mas não é intercambiável com uma chave REST.
URL base, versionamento e sua primeira escrita
A URL base é https://api.brevo.com/v3/. A versão fica no caminho, não em um cabeçalho, e a v3 é a geração atual. Todo caminho neste guia é relativo a essa base.
Para a primeira gravação de contato, use um endereço de teste sob seu controle, um ID de lista real e um ID de origem estável. Crie antes o atributo FNAME na Brevo. Com uma chave real, este exemplo grava na conta:
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 }'Troque 42 pelo ID da lista de teste. Releia o contato e confira ID, nome, lista e bloqueios. Uma resposta 2xx sozinha não comprova o resultado desejado.
Os endpoints que você vai usar de verdade
Contatos
POST /v3/contacts cria um contato. O corpo aceita email, attributes declarados, listIds e ext_id como chave estável da origem. updateEnabled: true atualiza um contato existente e getId: true solicita o ID resultante. A Brevo documenta esses campos. Reutilize o mesmo ext_id em todas as execuções e guarde a relação entre o ID de origem e o ID da Brevo.
Antes de escrever, decida qual sistema controla cada atributo e qual lista recebe contatos com consentimento. Indique listIds explicitamente. Nunca defina emailBlacklisted: false em atualizações comuns. A Brevo alerta que mudar o e-mail de um contato bloqueado remove o bloqueio. Trate essa mudança separadamente. Confira bloqueios e descadastros antes de adicionar alguém à lista de marketing. Ainda existe uma janela entre leitura e gravação; quando é necessária uma garantia rigorosa, verifique novamente antes de enviar.
As leituras passam por GET /v3/contacts, que pagina com limit (padrão 50, máximo 1000) e offset, e é compatível com modifiedSince e createdSince em UTC. Sincronizações incrementais devem se apoiar em modifiedSince em vez de percorrer a lista inteira. Note que o parâmetro filter só permite um operador de igualdade, então qualquer coisa mais expressiva pertence a um segmento.
Para carga em massa, POST /v3/contacts/import aceita fileUrl, fileBody ou jsonBody, aponta para listIds e roda de forma assíncrona, devolvendo um processId. A Brevo documenta um máximo de 10 MB por corpo e recomenda ficar perto de 8 MB, porque o parsing infla o payload. Informe notifyUrl para você saber o resultado em vez de ficar consultando.
E-mail transacional
POST /v3/smtp/email é o cavalo de batalha. Além de sender, to, subject e htmlContent, os campos que vale conhecer são:
templateIdcomparams, que substitui o conteúdo inline por um template da Brevo e suas substituições de variáveis. Os params de cada versão têm teto de 100 KB, e o acumulado, de 1000 KB.messageVersions, que envia variantes personalizadas em uma única chamada, com até 99 destinatários por versão.tagspodem ajudar a relacionar eventos de entrega ao fluxo da aplicação que os produziu.scheduledAtmaisbatchId, para envios futuros que você talvez queira cancelar em grupo.headers, em Title-Case, para cabeçalhos SMTP personalizados.
Uma única requisição aceita no máximo 2.000 destinatários. Para a diferença entre esse endpoint e o envio de campanhas, o guia de e-mail transacional traz a visão de estratégia de mensagens.
Campanhas de e-mail
POST /v3/emailCampaigns exige name e sender, além de exatamente uma fonte de conteúdo: htmlContent (mínimo de 10 caracteres, abaixo de 1 MB), htmlUrl ou templateId. O público vai em recipients como listIds ou segmentIds, e scheduledAt usa o formato UTC YYYY-MM-DDTHH:mm:ss.SSSZ. Rotas complementares cobrem envio imediato, envio de teste, atualização de status e obtenção do relatório da campanha.
Empresas, negócios e objetos
O CRM da Brevo tem dois caminhos de escrita que se sobrepõem, e escolher certo faz diferença.
As rotas de CRM são POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} e o conjunto equivalente para negócios. Elas são síncronas. Um PATCH devolve 204 assim que a mudança é aplicada.
A API de objetos é o caminho em massa: POST /v3/objects/{object_type}/batch/upsert aceita até 1000 registros e 1 MB por requisição, até 500 atributos por registro e até 10 registros de associação por tipo de objeto por registro. Ela devolve 202 com um processId, o que significa aceito, não aplicado.
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" } } ] }'O guia do Brevo CRM cobre o modelo de objetos do lado do operador.
SDKs oficiais
A Brevo mantém clientes na organização getbrevo do GitHub:
| Linguagem | Repositório |
|---|---|
| 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 |
O cliente Node é instalado como @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);O cliente Python é instalado com pip install brevo-python. Se você preferir não carregar uma dependência de SDK por causa de dois endpoints, a superfície HTTP crua é pequena o bastante para ser chamada direto, o que também mantém você isolado da rotatividade de versões do SDK:
O cliente Python é instalado com pip install brevo-python. Para poucos endpoints, você também pode chamar HTTP diretamente:
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 NoneUma atualização bem-sucedida pode retornar 204 sem corpo. Nesse caso, None indica que você deve reler o contato para obter o ID.
O exemplo pressupõe que quem chama já verificou consentimento e bloqueios na Brevo. Ele não solicita desbloqueio. Com uma chave real, o código grava na conta; teste primeiro com simulações. Conflitos de identidade podem produzir códigos 4xx diferentes. Leia a resposta e reconcilie os IDs, sem presumir sempre um 409.
Webhooks
Os webhooks são como você fica sabendo o que aconteceu depois de um envio. POST /v3/webhooks cria um, com url, events, type e, opcionalmente, channel (email ou sms), batched, headers personalizados e um objeto auth.
Existem três tipos de webhook, com vocabulários de evento distintos:
- Transacional:
sent,request,delivered,hardBounce,softBounce,blocked,spam,invalid,deferred,click,opened,uniqueOpened,unsubscribed. - Marketing:
spam,opened,click,hardBounce,softBounce,unsubscribed,listAddition,delivered,contactUpdated,contactDeleted. - Entrada:
inboundEmailProcessedereply, que exigem adicionalmente umdomain.
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" }'Para alterações de contatos, registre um webhook de marketing com contactUpdated. A documentação da Brevo chama o evento recebido de contact_updated. Autentique o receptor com o token bearer auth ou os cabeçalhos personalizados do guia de segurança. Verifique o segredo antes de processar o evento e não o registre em logs.
Guarde uma chave de deduplicação com tipo de evento, identificador do contato e data/hora pelo período necessário para cobrir novas entregas. Só o horário não é único. Entregas podem se repetir ou chegar fora de ordem. Depois de um evento, leia o contato atual e reconcilie com sua fonte confiável. Defina uma janela de replay, descarte eventos antigos após a recuperação e faça verificações incrementais periódicas. Um webhook indica mudança; sozinho não cria uma sincronização bidirecional. O guia de conectores explica a escolha.
Limites de taxa e tratamento de erros
Os limites de taxa da Brevo são por endpoint e por nível de plano, e a distância entre endpoints é enorme.
| Endpoint | General (todos os planos) | Advanced (Professional e 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 |
| Endpoints sem limite próprio | 100 RPH | 200 RPH |
A Brevo documenta x-sib-ratelimit-limit, x-sib-ratelimit-remaining e x-sib-ratelimit-reset nos endpoints limitados. Leia-os também em respostas de sucesso. Com 429, espere o reinício e repita apenas dentro de limites fixos de tentativas e espera. O valor reset é uma duração na unidade da janela, geralmente segundos, não um 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 a espera necessária exceder 60 segundos, a função retorna 429. Agende uma nova tentativa para mais tarde.
Este auxiliar serve apenas para requisições cuja repetição você conferiu ser segura. Após um erro de envio ambíguo, reconcilie o identificador da mensagem antes de enviar de novo. Registre status e cabeçalhos de limite, nunca a chave API ou dados pessoais.
Não repita um 4xx sem análise. A referência para criar contatos lista 400 e 425; um conflito de identificação não garante 409. Após um 5xx em uma gravação, confira se ela já ocorreu.
Qual status voltou?
-
429
Espere dentro de um limite
Use a duração de reset e pare após poucas tentativas.
-
5xx após gravar
Confira se aplicou
O servidor pode ter gravado antes do erro.
-
400
Corrija a requisição
Geralmente ela está incorreta.
-
Conflito de identificadores
Leia e reconcilie os IDs
A API não promete um código específico.
Testar sem enviar e-mail
Adicione o cabeçalho X-Sib-Sandbox com o valor drop a um envio transacional. A Brevo valida a requisição, devolve 201 com um messageId, não entrega nada e não grava nenhum log de e-mail.
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>" }'Entenda o que isso prova e o que não prova. O modo sandbox valida apenas o formato da requisição. Ele não diz nada sobre autenticação do remetente, renderização de template ou entregabilidade. Mantenha uma conta Brevo separada para testes de integração de qualquer coisa que toque contatos ou dados de CRM, porque o modo sandbox cobre o envio e não o resto da API.
Antes de sincronizar contatos
- Escolha um ID de origem estável para
ext_ide guarde o ID da Brevo retornado. - Verifique os atributos antes de gravar. A Brevo pode ignorar os desconhecidos ou de tipo incorreto; releia um contato de teste.
- Use uma lista explícita e uma prova confiável de consentimento. Ignore bloqueios e descadastros existentes.
- Conte separadamente registros aceitos, aplicados, ignorados e com erro. Um 202 em lotes ou objetos confirma apenas aceitação.
- Use webhooks de marketing como sinais e reconcilie o estado atual. Autentique, deduplique e limite o replay.
- Monitore os cabeçalhos de limite e pare após poucas tentativas.
Tajo se concentra em sincronizar dados comerciais com a Brevo. Se você desenvolver essa camada, o guia de conectores Brevo compara caminhos e falhas.
Perguntas frequentes
Qual é a URL base da API da Brevo?
Todas as chamadas REST da Brevo vão para https://api.brevo.com/v3/. A versão faz parte do caminho, e a v3 é a geração atual, então um endpoint como o de envio transacional tem o caminho completo https://api.brevo.com/v3/smtp/email.
Como você se autentica na API da Brevo?
Envie sua chave em um cabeçalho HTTP chamado api-key. A Brevo não usa um cabeçalho Authorization nem Bearer para chaves de API padrão. As chaves são geradas nas configurações da conta, em SMTP e API, chaves de API, e o valor aparece uma única vez.
Quais são os limites de taxa da API da Brevo?
Os limites são por endpoint e por nível de limite de taxa. No nível general, que todo plano recebe, o endpoint de envio transacional permite 1.000 requisições por segundo, os endpoints de contatos permitem 10 por segundo, e os endpoints sem limite próprio ficam limitados a 100 requisições por hora. Os planos Professional e Enterprise podem usar o nível advanced, e o Enterprise o nível extended. Ultrapassar um limite devolve 429.
A Brevo tem SDKs oficiais?
Sim. A Brevo publica clientes para Node.js, Python, PHP, Java, C#, Go e Ruby na organização getbrevo do GitHub. O pacote Node é @getbrevo/brevo no npm e o pacote Python é brevo-python no PyPI.
Como testo a API da Brevo sem enviar e-mail de verdade?
Adicione o cabeçalho X-Sib-Sandbox com o valor drop a um envio transacional. A Brevo valida a requisição, devolve 201 com um messageId, não envia nada e não grava nenhum log de e-mail. Ela só verifica o formato da requisição, não a entregabilidade.
Quais eventos de webhook a Brevo oferece?
Webhooks transacionais incluem entregas, falhas e descadastros. Os de marketing incluem contactUpdated e contactDeleted. Para eventos de entrada, a Brevo documenta inboundEmailProcessed.
Como uma atualização de contato preserva um descadastro?
Use um ID de origem estável e atributos declarados. Não defina emailBlacklisted como false em atualizações rotineiras. Confira o contato e a fonte de consentimento antes de incluí-lo numa lista de marketing.
Um webhook basta para uma sincronização bidirecional?
Não. Autentique o receptor, elimine eventos duplicados e reconcilie eventos perdidos ou fora de ordem com uma fonte confiável. Um webhook sinaliza uma mudança, mas não prova que os sistemas concordam.