Documentação API
Referência completa para integrar qualquer sistema interno da B20 à B20Mail. A API é REST, aceita JSON e autentica por API Key via header.
VISÃO GERAL
A B20Mail é o único ponto de acesso ao Amazon SES na B20. Nenhum sistema deve configurar credenciais AWS diretamente — todos os envios passam por esta API.
https://api.b20mail.com| Item | Valor |
|---|---|
| Protocolo | HTTPS (HTTP aceito em dev local) |
| Formato | JSON (Content-Type: application/json) |
| Autenticação | API Key via header x-api-key |
| Provider | Amazon SES via AWS SDK PHP |
| Logs | MySQL + arquivo mensal em logs/ |
AUTENTICAÇÃO
Toda requisição às rotas protegidas deve incluir o header x-api-key com a chave do projeto. Sua chave fica no painel, na aba API Keys — é uma sequência de 64 caracteres hexadecimais.
# Header obrigatório em todas as requisições autenticadas x-api-key: <cole aqui a chave de 64 caracteres do seu projeto>
- Somente no servidor (server-to-server). Use a chave no backend do seu sistema. Nunca em JavaScript de navegador, app mobile ou qualquer código que chegue ao usuário final — a API recusa chamadas de navegador (CORS) justamente para impedir esse tipo de vazamento.
- Sempre via variável de ambiente. Carregue a chave de
.env/ variável de ambiente. Nunca escreva a chave direto no código-fonte nem faça commit dela em repositório (mesmo privado). - Nunca em logs, prints ou tickets. Ao pedir suporte ou compartilhar uma tela, oculte a chave.
- Rotacione se vazar. Se suspeitar de exposição, desative o projeto no painel e gere uma nova chave — a antiga deixa de funcionar imediatamente.
RATE LIMITING
Cada API Key possui um limite de requisições por minuto configurado no .env (padrão: 60 req/min). Ao exceder, a API retorna 429 Too Many Requests.
| Variável .env | Padrão | Descrição |
|---|---|---|
| RATE_LIMIT_PER_MINUTE | 60 | Máximo de requisições por minuto por chave |
FORMATO DE RESPOSTA
Todas as respostas seguem o mesmo envelope JSON:
{
"success": true | false,
"message": "Mensagem descritiva",
"request_id": "uuid-para-rastreio",
// campos extras dependendo da rota
}
ENDPOINTS
# Resposta 200 { "success": true, "message": "pong", "version": "1.0.0", "env": "local", "time": "2026-05-18 16:00:00" }
from no formato Nome <[email protected]>. Além disso, o seu domínio precisa estar obrigatoriamente cadastrado e verificado na aba "REMETENTES" do seu painel.
Body da Requisição
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
| to | string | Sim | Email do destinatário. |
| from | string | Sim | Remetente ("Nome <[email protected]>"). Só funciona se o domínio foi cadastrado e validado no seu painel. |
| subject | string | Sim | Assunto do email (max 200 chars) |
| template | string | Sim* | Nome do template: welcome, recover-password, notification. *Obrigatório se não enviar html |
| html | string | Não | Envie seu próprio HTML puro aqui. A API aceita o HTML completo e ainda injeta suas variáveis nele. Sobrescreve o template. |
| type | string | Não | transactional ou marketing (padrão: transactional). Valores diferentes retornam 422. |
| variables | object | Não | Variáveis para renderizar no template ou no seu html customizado. |
# Exemplo completo POST /send-email Content-Type: application/json { "from": "Sua Empresa <[email protected]>", "to": "[email protected]", "subject": "Bem-vindo à B20!", "template": "welcome", "type": "transactional", "variables": { "name": "João Silva", "link": "https://seusistema.com/comecar" } } // Response 200 — o e-mail entra na FILA e é enviado pelo worker em segundos. { "success": true, "message": "Email enfileirado com sucesso", "request_id": "a1b2c3-...", "message_id": null } // message_id fica null na resposta (o envio é assíncrono). Consulte o // status final e o message_id da AWS depois via GET /logs.
Query Parameters
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
| limit | int | Não | Quantidade de registros (Padrão: 50. Máx: 100). |
| offset | int | Não | Paginação (Padrão: 0). |
| status | string | Não | Filtrar por status: sent, failed, queued. |
# Exemplo de Requisição (cURL) curl -X GET "https://api.b20mail.com/logs?limit=100&status=failed" \ -H "x-api-key: SUA_CHAVE_AQUI" // Response 200 { "success": true, "message": "Logs recuperados", "request_id": "xyz-...", "count": 100, "limit": 100, "offset": 0, "logs": [ { "request_id": "abc-...", "recipient": "[email protected]", "status": "failed", "error_message": "Bounce detectado", "created_at": "2026-06-15 14:00:00" } ] }
Query Parameters
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
| period | string | Não | Período analisado: 24h, 7d, 30d, 90d (Padrão: 30d). |
# Exemplo de Requisição GET /stats?period=7d // Response 200 { "success": true, "message": "Stats OK", "request_id": "xyz-...", "period": "7d", "overview": { "total": 1462, "sent": 1450, "failed": 12, "opened": 840 }, "timeline": [ { "label": "2026-06-12", "total": 502, "sent": 500, "failed": 2, "opened": 280, "transactional": 300, "marketing": 200 }, { "label": "2026-06-13", "total": 960, "sent": 950, "failed": 10, "opened": 560, "transactional": 650, "marketing": 300 } ] }
TEMPLATES DISPONÍVEIS
| Template | Uso | Variáveis principais |
|---|---|---|
| welcome | Boas-vindas ao novo usuário | name, link |
| recover-password | Recuperação de senha | name, link |
| notification | Notificação genérica | title, message, name, link (opcional) |
| default | Layout genérico (fallback automático) | subject, title, message, link, button_text, company_name |
variables da requisição. Nomes diferentes não serão substituídos e o e-mail sairá com o campo em branco.ENVIANDO SEU PRÓPRIO HTML (CUSTOM)
Se você não quiser usar os templates da B20Mail, você pode desenhar e enviar o seu próprio código HTML cru. Basta omitir a variável template e preencher a variável html na requisição JSON.
{{sua_var}} dentro dele e enviar no objeto variables. A B20Mail vai procurar e substituir as variáveis automaticamente antes de disparar.SINTAXE DE VARIÁVEIS
Use {{variavel}} nos templates. As variáveis são sanitizadas automaticamente contra XSS.
<!-- No template HTML --> <p>Olá, {{name}}!</p> <a href="{{link}}">Recuperar senha</a>
TUTORIAL: CLASSE DE INTEGRAÇÃO COMPLETA (PHP)
Abaixo apresentamos um exemplo de classe Mailer.php pronta para uso em produção, ideal para projetos PHP (puros ou frameworks). Ela demonstra como ler as configurações do .env, processar o envio inteligente (priorizando arquivos HTML locais com fallback para a B20Mail) e tratar retornos e logs de erro.
1. Configuração do .env
# Arquivo .env do seu projeto B20MAIL_API_URL="https://api.b20mail.com" B20MAIL_API_KEY="SUA_CHAVE_AQUI" MAIL_FROM_ADDRESS="Sua Empresa <[email protected]>"
2. Classe Mailer.php
<?php /** * Classe de Integração SaaS — B20Mail */ class Mailer { /** * Dispara um e-mail via B20Mail API */ public static function send(string $to, string $subject, array $variables = [], string $templateName = 'default', string $type = 'transactional'): bool { $apiKey = trim($_ENV['B20MAIL_API_KEY'] ?? getenv('B20MAIL_API_KEY') ?? '', '"\''); if (empty($apiKey)) { error_log('B20MAIL_API_KEY não configurada'); return false; } $mailFrom = trim($_ENV['MAIL_FROM_ADDRESS'] ?? getenv('MAIL_FROM_ADDRESS') ?? '', '"\''); $payload = [ 'from' => $mailFrom ? $mailFrom : 'Sua Empresa <[email protected]>', 'to' => $to, 'subject' => $subject, 'type' => $type, 'variables' => array_merge($variables, ['email' => $to]) ]; // Tenta carregar o template HTML da pasta local. Se não achar, delega pro template global da B20Mail. $templateFile = __DIR__ . '/views/emails/' . $templateName . '.html'; if (file_exists($templateFile)) { $payload['html'] = file_get_contents($templateFile); } else { $payload['template'] = $templateName; } $apiUrl = trim($_ENV['B20MAIL_API_URL'] ?? getenv('B20MAIL_API_URL') ?? '', '"\''); $apiUrl = rtrim($apiUrl ?: 'https://api.b20mail.com', '/'); $ch = curl_init($apiUrl . '/send-email'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'x-api-key: ' . $apiKey, 'Content-Type: application/json' ], CURLOPT_POSTFIELDS => json_encode($payload) ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode >= 200 && $httpCode < 300) { return true; } error_log('B20Mail Erro (HTTP ' . $httpCode . '): ' . $response); return false; } }
EXEMPLOS DE INTEGRAÇÃO BÁSICA
PHP / cURL
<?php $ch = curl_init('https://api.b20mail.com/send-email'); $payload = json_encode([ 'from' => 'Sua Empresa <[email protected]>', 'to' => '[email protected]', 'subject' => 'Bem-vindo!', 'template' => 'welcome', 'type' => 'transactional', 'variables' => ['name' => 'João', 'link' => 'https://seusistema.com/comecar'], ]); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => [ 'x-api-key: SUA_CHAVE_AQUI', 'Content-Type: application/json', ], ]); $response = json_decode(curl_exec($ch), true); curl_close($ch);
JavaScript / Fetch
async function sendEmail() { const response = await fetch('https://api.b20mail.com/send-email', { method: 'POST', headers: { 'x-api-key': 'SUA_CHAVE_AQUI', 'Content-Type': 'application/json', }, body: JSON.stringify({ from: 'Sua Empresa <[email protected]>', to: '[email protected]', subject: 'Bem-vindo!', template: 'welcome', type: 'transactional', variables: { name: 'João', link: 'https://seusistema.com/comecar' }, }), }); const data = await response.json(); }
Python / requests
import requests payload = { "from": "Sua Empresa <[email protected]>", "to": "[email protected]", "subject": "Bem-vindo!", "template": "welcome", "type": "transactional", "variables": {"name": "João", "link": "https://seusistema.com/comecar"}, } response = requests.post( "https://api.b20mail.com/send-email", headers={ "x-api-key": "SUA_CHAVE_AQUI", "Content-Type": "application/json", }, json=payload, ) data = response.json()
GERAR API KEY
As API Keys são gerenciadas diretamente pelo Painel Administrativo. Acesse a aba API KEYS no dashboard para:
| Ação | Como fazer |
|---|---|
| Criar nova key | O admin cria o projeto e gera a chave automaticamente via dashboard. |
| Ver keys ativas | Aba API KEYS no dashboard — mostra label, plano, status e métricas. |
| Ativar/Desativar | Botão de toggle na tabela de keys (apenas superadmin). |
| Quickstart | Ao selecionar uma key, o painel exibe código pronto em Node.js, PHP e cURL com a chave injetada. |
WEBHOOKS
A B20Mail pode notificar seu sistema em tempo real quando um e-mail sofre bounce (rejeição) ou complaint (spam). Basta cadastrar um endpoint HTTPS no painel ou via API.
Eventos Disponíveis
| Evento | Trigger | Descrição |
|---|---|---|
| email.bounced | AWS SNS Bounce | E-mail rejeitado pelo servidor destino |
| email.complained | AWS SNS Complaint | Destinatário marcou como spam |
| webhook.test | Painel Admin | Evento de teste para validar conectividade |
Formato do Payload
Todos os webhooks são enviados como POST com body JSON:
{
"event": "email.bounced",
"timestamp": "2026-06-29T21:00:00Z",
"data": {
"message_id": "0100018ff123abc-abcd-1234",
"request_id": "a1b2c3d4e5f6-123456",
"recipient": "[email protected]",
"subject": "Bem-vindo!",
"type": "Permanent",
"diagnostic": "Bounce: 550 5.1.1 User unknown",
"original_event": "Bounce"
}
}
Verificação de Assinatura (HMAC-SHA256)
Cada webhook inclui headers de assinatura para garantir autenticidade:
| Header | Descrição |
|---|---|
| X-B20Mail-Event | Tipo do evento (email.bounced, etc.) |
| X-B20Mail-Timestamp | Unix timestamp do envio |
| X-B20Mail-Signature | Assinatura sha256=HMAC(timestamp.payload, secret) |
Exemplos de Handler
PHP
<?php $secret = 'whsec_seu_secret_aqui'; $payload = file_get_contents('php://input'); $timestamp = $_SERVER['HTTP_X_B20MAIL_TIMESTAMP'] ?? ''; $signature = $_SERVER['HTTP_X_B20MAIL_SIGNATURE'] ?? ''; // Validar assinatura $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $payload, $secret); if (!hash_equals($expected, $signature)) { http_response_code(401); die('Assinatura inválida'); } $data = json_decode($payload, true); $event = $data['event'] ?? ''; match($event) { 'email.bounced' => handleBounce($data['data']), 'email.complained' => handleComplaint($data['data']), default => null, }; http_response_code(200); echo 'OK';
Node.js / Express
const crypto = require('crypto'); app.post('/webhook/b20mail', (req, res) => { const secret = process.env.B20MAIL_WEBHOOK_SECRET; const timestamp = req.headers['x-b20mail-timestamp']; const signature = req.headers['x-b20mail-signature']; const payload = JSON.stringify(req.body); const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(timestamp + '.' + payload) .digest('hex'); if (expected !== signature) return res.status(401).send('Invalid'); const { event, data } = req.body; console.log(`Evento: ${event}`, data); res.status(200).send('OK'); });
Python / Flask
import hmac, hashlib, json from flask import Flask, request app = Flask(__name__) @app.route('/webhook/b20mail', methods=['POST']) def webhook(): secret = "whsec_seu_secret".encode() payload = request.get_data(as_text=True) timestamp = request.headers.get('X-B20Mail-Timestamp', '') signature = request.headers.get('X-B20Mail-Signature', '') expected = 'sha256=' + hmac.new( secret, (timestamp + '.' + payload).encode(), hashlib.sha256 ).hexdigest() if not hmac.compare_digest(expected, signature): return 'Invalid', 401 data = json.loads(payload) print(f"Evento: {data['event']}", data['data']) return 'OK', 200
Como Cadastrar seu Endpoint
- Acesse o Dashboard e clique na aba
WEBHOOKSna barra lateral - Clique em "+ NOVO ENDPOINT"
- Selecione o projeto (API Key) vinculado
- Informe a URL HTTPS do seu endpoint receptor
- Marque os eventos que deseja receber (
email.bounced,email.complained) - Clique em "CRIAR ENDPOINT"
- Copie o secret HMAC exibido — ele será mostrado apenas uma vez
B20MAIL_WEBHOOK_SECRET). Se perdido, remova o endpoint e crie um novo para gerar um novo secret.CÓDIGOS DE ERRO
| HTTP | Significado | Causa comum |
|---|---|---|
| 400 | Bad Request | JSON malformado no body |
| 401 | Unauthorized | Header x-api-key ausente ou inválido |
| 404 | Not Found | Rota inexistente (confira a URL) |
| 405 | Method Not Allowed | Método HTTP errado para a rota. O header Allow da resposta informa quais são aceitos — /send-email só aceita POST. |
| 422 | Unprocessable | Email inválido, template inexistente, campo ausente |
| 429 | Too Many Requests | Rate limit excedido para esta chave |
| 500 | Server Error | Falha na AWS SES ou banco de dados |
GET antes de usá-la. Como
/send-email aceita apenas POST, elas recebem
405 — o que significa "a rota existe, use outro método",
e não que o endpoint esteja fora do ar. Para um teste de disponibilidade,
use GET /ping, que é público e responde 200.