Como enviar e-mails transacionais com Node.js, Python e cURL

Exemplos prontos para copiar e enviar e-mails por uma API REST com Node.js, Python, PHP e linha de comando, com timeouts, tratamento de erros e um helper reutilizável.

E-mails transacionais são as mensagens que o seu produto envia porque um usuário fez algo: se cadastrou, redefiniu a senha, pagou uma fatura, foi convidado para um workspace. Eles precisam sair rápido, do seu próprio domínio, e sem deixar lenta a requisição que os disparou.

Este artigo mostra como enviá-los pela API REST da Inbixio nas linguagens que as equipes mais usam e depois transforma a chamada avulsa em um pequeno helper que você pode colocar em qualquer código.

A requisição, em um só lugar

Todos os exemplos abaixo enviam a mesma requisição HTTP:

  • Método e URL: POST https://backend.inbixio.com/api/send-email/, incluindo a barra final
  • Autenticação: um header X-API-Key com uma chave que tenha a permissão Send (enviar)
  • Corpo: JSON com to_email, subject e message (HTML)

Uma chamada bem-sucedida retorna 200 com {"message": "Email queued successfully."}. Se você ainda não criou uma chave nem conectou um servidor SMTP, comece pelo guia de início rápido de cinco minutos.

Em todos os exemplos, a chave é lida de uma variável de ambiente chamada INBIXIO_API_KEY. Mantenha-a no servidor; nunca a envie para um navegador ou app mobile.

cURL

Útil para testar uma chave, depurar a partir de um servidor ou chamar de um script shell ou cron job:

curl -sS -X POST https://backend.inbixio.com/api/send-email/ \
  -H "X-API-Key: $INBIXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 15 \
  -d '{
    "to_email": "ada@example.com",
    "subject": "Your invoice is ready",
    "message": "<p>Hi Ada, your invoice for October is ready.</p>"
  }'

-sS esconde a barra de progresso, mas continua exibindo os erros, e --max-time impede que um script fique travado se a rede travar.

Node.js

O Node.js 18 e versões posteriores já vêm com fetch, então você não precisa de uma biblioteca HTTP:

// send-email.js
const INBIXIO_URL = 'https://backend.inbixio.com/api/send-email/';

export async function sendEmail({ to, subject, html }) {
  const res = await fetch(INBIXIO_URL, {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.INBIXIO_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ to_email: to, subject, message: html }),
    signal: AbortSignal.timeout(15_000),
  });

  const body = await res.json().catch(() => ({}));
  if (!res.ok) {
    const reason = body.error || body.detail || res.statusText;
    throw new Error(`Inbixio ${res.status}: ${reason}`);
  }
  return body; // { message: 'Email queued successfully.' }
}

E usando-o em uma rota do Express:

app.post('/signup', async (req, res) => {
  const user = await createUser(req.body);
  await sendEmail({
    to: user.email,
    subject: 'Welcome aboard',
    html: `<h1>Welcome, ${escapeHtml(user.name)}!</h1><p>Your account is ready.</p>`,
  });
  res.status(201).json({ id: user.id });
});

Repare no escapeHtml. O campo message é renderizado como HTML, então tudo o que um usuário digitou, como o nome dele, precisa ser escapado antes de entrar no corpo.

Python

Com requests:

# send_email.py
import os
import requests

INBIXIO_URL = "https://backend.inbixio.com/api/send-email/"

class EmailError(Exception):
    pass

def send_email(to: str, subject: str, html: str) -> dict:
    res = requests.post(
        INBIXIO_URL,
        headers={"X-API-Key": os.environ["INBIXIO_API_KEY"]},
        json={"to_email": to, "subject": subject, "message": html},
        timeout=15,
    )
    if not res.ok:
        try:
            body = res.json()
        except ValueError:
            body = {}
        reason = body.get("error") or body.get("detail") or res.reason
        raise EmailError(f"Inbixio {res.status_code}: {reason}")
    return res.json()

Passar json= define o header Content-Type para você. Sempre defina um timeout: por padrão, o requests espera para sempre.

No Django, chame a função depois do commit da transação no banco de dados, para nunca enviar e-mail sobre um registro que sofreu rollback:

from django.db import transaction

transaction.on_commit(lambda: send_email(user.email, "Welcome!", render_welcome(user)))

PHP

<?php
function send_email(string $to, string $subject, string $html): array {
    $ch = curl_init('https://backend.inbixio.com/api/send-email/');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_HTTPHEADER => [
            'X-API-Key: ' . getenv('INBIXIO_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'to_email' => $to,
            'subject'  => $subject,
            'message'  => $html,
        ]),
    ]);
    $body = json_decode(curl_exec($ch), true) ?? [];
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($status !== 200) {
        throw new RuntimeException("Inbixio $status: " . ($body['error'] ?? $body['detail'] ?? 'request failed'));
    }
    return $body;
}

Envio para várias pessoas

Cada requisição entrega para um único to_email. Para notificar uma lista de usuários, envie uma requisição por destinatário. É isso que você quer em e-mails transacionais de qualquer forma: cada usuário recebe a própria mensagem, ninguém vê o endereço de ninguém e um endereço inválido não bloqueia os demais.

Mantenha a concorrência moderada para não sobrecarregar o seu próprio servidor SMTP. Um pequeno pool de 5–10 requisições paralelas é suficiente para a maioria dos apps.

Não envie no caminho da requisição

Chamar a API é rápido, mas ainda é uma ida e volta pela rede. Para tudo o que não for urgente, coloque o envio em um job em segundo plano (BullMQ, Celery, Sidekiq, uma fila na nuvem) para que:

  • a página do usuário carregue sem esperar pelo e-mail,
  • um erro temporário possa ser repetido sem que o usuário perceba, e
  • um pico repentino, como uma grande importação que dispara milhares de e-mails de boas-vindas, seja escoado em ritmo constante.

Para códigos OTP e redefinições de senha, os usuários estão esperando ativamente, então envie na hora e mostre um erro amigável se falhar. O artigo como lidar com erros e limites da API de e-mail explica quais erros repetir e quais mostrar.

Checklist antes de publicar

  • A chave de API vem de uma variável de ambiente ou de um gerenciador de segredos, e a chave tem apenas a permissão Send.
  • Todo valor fornecido pelo usuário no HTML é escapado.
  • As requisições têm um timeout de 10–15 segundos.
  • Respostas diferentes de 200 são registradas em log com o corpo da resposta, para que as mensagens de error e detail não se percam.
  • Seu domínio de envio tem SPF, DKIM e DMARC configurados, para que os e-mails cheguem à caixa de entrada.

Envie com a Inbixio

Conecte seu próprio servidor SMTP, crie uma chave de API com permissões restritas e envie de qualquer linguagem com uma única requisição HTTPS.

Criar conta Ver preços