Documentação Viva e Guia de Integração Multi-Tenant (Server-to-Server)
Atualizado em 18/05/2026
Acesso Administrativo (NOC)O gateway adota o máximo rigor de arquitetura corporativa. Nenhuma requisição permite credenciais em texto claro na URI (IPs não importam no nosso Traffic Shaping).
Para interagir com as rotas abaixo, certifique-se de injetar os seguintes Headers:
X-Public-Key: A sua chave pública injetada pelo provedor no ato do cadastro.X-Timestamp: Hora atual Unix (Segundos) para prevenção de interceptações temporais (desvio < 5 min).X-Nonce: UUID ou string única (Single-Use Token protegido atômicamente no Redis contra Replay Attacks).X-Signature: O hash HMAC SHA256 processado com a sua `Secret-Key`. A string do hash antes da criptografia deve ser rigorosamente construída na seguinte ordem:// Exemplo de Formulação do Hash (PHP)
$stringBase = $timestamp . $nonce . json_encode($body) . json_encode($recipients) . json_encode($attachments);
$x_signature = hash_hmac('sha256', $stringBase, $secretKeyDesteTenant);
Filtragem de Tráfego: O gateway possui uma tranca extra por IP para prevenir ataques DDoS originados da internet aberta. Apenas os IPs explicitados na chave hosts contida no arquivo config/config.php terão seu handshake efetivado na API.
Proteção Sistêmica de Retorno: Garantia absoluta de ausência de renderização HTML. Qualquer colapso (404 Not Found por rota desativada, ou 500 Error por SMTP falhando com TransportException) gerará uma devolução limpa em formato application/json — mesmo que o seu client não envie o request indicando Accept: application/json. O parseamento no seu frontend/Worker nunca mais irá quebrar.
O EmailGw oferece um painel de controle centralizado para administradores, permitindo o monitoramento de saúde do sistema em tempo real.
/admin/email-monitor com filtros por tenant, provider, email do destinatário, status e período, incluindo inspeção de logs de erro por envio.Seja você usando Insomnia, Postman ou cURL, lembre-se de que a rota agora aceita requisições de qualquer domínio via API, bastando enviar o identificador do tenant no subdomínio da URL (ex: https://{seu-tenant}.opsmailgw.com.br/...).
{
"smtp_config": {
"transport": "smtp",
"host": "smtp.mailgun.org",
"port": 587,
"username": "api@teste.com",
"password": "senha_segura",
"encryption": "tls",
"from_address": "joao@teste.com"
},
"body": {
"subject": "Sua Fatura Chegou!",
"content_html": "Sua Fatura está Aqui!
",
"content_text": "Sua Fatura está Aqui!"
},
"recipients": [
{"email": "cliente1@dominio.com", "name": "Cliente VIP"},
{"email": "cliente2@dominio.com", "name": "Cliente Silver"}
]
}
{
"smtp_config": {
"transport": "smtp",
"host": "smtp.mailgun.org",
"port": 587,
"username": "api@teste.com",
"password": "senha_segura",
"encryption": "tls",
"from_address": "joao@teste.com"
},
"test_recipient": "seu_email_pessoal@dominio.com"
}
Endpoints para extração de métricas e logs de e-mails enviados, permitindo a construção de dashboards e relatórios detalhados.
GET /v1/stats/summary)Retorna quantidades totais, taxa de sucesso e tempo médio de execução em um período especificado.
curl --location --request GET 'https://{seu-tenant}.opsmailgw.com.br/v1/stats/summary?period=day&date=2023-01-15' \
--header 'X-Public-Key: sua_chave_publica_aqui' \
--header 'X-Timestamp: 1711285000' \
--header 'X-Nonce: token_nonce_unico_aqui' \
--header 'X-Signature: seu_hash_hmac_aqui' \
--header 'Accept: application/json'
Parâmetros de Query:
period: day, month, year, hour (default: day)date: Data de referência (YYYY-MM-DD)Exemplo de Resposta:
{
"total_sent": 1420,
"total_failed": 12,
"success_rate_percent": 99.16,
"average_execution_time_ms": 412.35
}
GET /v1/stats/timeline)Série temporal detalhada graficamente (enviados vs falhas).
curl --location --request GET 'https://{seu-tenant}.opsmailgw.com.br/v1/stats/timeline?interval=hour&range=24' \
--header 'X-Public-Key: sua_chave_publica_aqui' \
--header 'X-Timestamp: 1711285000' \
--header 'X-Nonce: token_nonce_unico_aqui' \
--header 'X-Signature: seu_hash_hmac_aqui' \
--header 'Accept: application/json'
Parâmetros de Query:
interval: hour, minute (default: hour)range: Retroativo em horas (default: 24)GET /v1/stats/success)Extração dos Envios de E-mail consolidados (success) com paginação.
curl --location --request GET 'https://{seu-tenant}.opsmailgw.com.br/v1/stats/success?start=2023-01-01%2000:00:00&end=2023-01-31%2023:59:59' \
--header 'X-Public-Key: sua_chave_publica_aqui' \
--header 'X-Timestamp: 1711285000' \
--header 'X-Nonce: token_nonce_unico_aqui' \
--header 'X-Signature: seu_hash_hmac_aqui' \
--header 'Accept: application/json'
Parâmetros de Query:
start: Filtro inicial (YYYY-MM-DD HH:MM:SS)end: Filtro final (YYYY-MM-DD HH:MM:SS)GET /v1/stats/errors)Relatório com métricas de troubleshooting.
curl --location --request GET 'https://{seu-tenant}.opsmailgw.com.br/v1/stats/errors?start=2023-01-01%2000:00:00&end=2023-01-31%2023:59:59' \
--header 'X-Public-Key: sua_chave_publica_aqui' \
--header 'X-Timestamp: 1711285000' \
--header 'X-Nonce: token_nonce_unico_aqui' \
--header 'X-Signature: seu_hash_hmac_aqui' \
--header 'Accept: application/json'
Parâmetros de Query:
start: Filtro inicial (YYYY-MM-DD HH:MM:SS)end: Filtro final (YYYY-MM-DD HH:MM:SS)// ===== CONFIG =====
const publicKey = "SUACHAVEPUBLICAAQUI";
const secret = "SUACHAVEPRIVADAAQUI";
const domain = "LOCAL";
// ===== VALIDATION =====
if (!publicKey || !secret) {
throw new Error("Defina 'public_key' e 'secret' no environment.");
}
// ===== TIMESTAMP (segundos) =====
const timestamp = Math.floor(Date.now() / 1000);
// ===== NONCE (UUID v4) =====
function uuidv4() {
return crypto.randomUUID();
}
const nonce = uuidv4();
// ===== RAW BODY (EXATAMENTE COMO SERÁ ENVIADO) =====
let rawBody = insomnia.request.body ? insomnia.request.body.raw : "";
if (!rawBody) {
rawBody = "";
}
// ⚠️ NÃO ALTERAR O JSON (sem stringify, sem parse)
// Ele precisa ser exatamente igual ao enviado
// ===== PAYLOAD =====
const payload = timestamp + nonce + rawBody;
// ===== HMAC SHA256 =====
async function generateSignature() {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signatureBuffer = await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(payload)
);
// ===== BASE64 =====
const signatureBase64 = btoa(
String.fromCharCode(...new Uint8Array(signatureBuffer))
);
// ===== SET HEADERS =====
insomnia.request.headers.upsert({ key: "X-Public-Key", value: publicKey });
insomnia.request.headers.upsert({ key: "X-Timestamp", value: timestamp.toString() });
insomnia.request.headers.upsert({ key: "X-Nonce", value: nonce });
insomnia.request.headers.upsert({ key: "X-Signature", value: signatureBase64 });
insomnia.request.headers.upsert({ key: "X-Domain", value: domain });
insomnia.request.headers.upsert({ key: "Content-Type", value: "application/json" });
// ===== DEBUG (opcional) =====
console.log("Payload:", payload);
console.log("Signature:", signatureBase64);
}
// Executa
generateSignature();