Pular para o conteúdo
couryo

Guia do desenvolvedorIntegrações

Node.js com Express

Enviar e-mail pelo Couryo numa API Node.js com Express, com idempotência, tratamento de erros e o webhook assinado lido com o corpo cru.

Ver em Markdown

Um servidor Express que envia o e-mail de boas-vindas no cadastro e recebe os eventos de entrega por webhook, com o SDK Node.

1. Instale#

Terminal
npm install express @couryo/node
.env
COURYO_API_KEY=ck_test_...
COURYO_WEBHOOK_SECRET=whsec_...

Rode com node --env-file=.env server.js (Node 20.6 ou mais novo lê o .env sozinho).

2. Enviar no cadastro#

server.js
import express from "express";
import { Couryo, CouryoError, verifyWebhook, WebhookVerificationError } from "@couryo/node";

const app = express();
const couryo = new Couryo(); // lê COURYO_API_KEY

app.post("/cadastro", express.json(), async (req, res) => {
  const { email, nome } = req.body;
  const usuario = await criarUsuario({ email, nome }); // a sua função

  try {
    const enviado = await couryo.emails.send(
      {
        from: "Exemplo <oi@exemplo.com.br>",
        to: email,
        subject: `Boas-vindas, ${nome}!`,
        html: `<p>Oi, ${nome}! Sua conta está pronta.</p>`,
        text: `Oi, ${nome}! Sua conta está pronta.`,
        tags: { tipo: "boas-vindas" },
      },
      // Se esta rota rodar duas vezes para o mesmo usuário, o e-mail sai uma vez só.
      { idempotencyKey: `boas-vindas-${usuario.id}` },
    );
    res.status(201).json({ usuario: usuario.id, email_id: enviado.id });
  } catch (e) {
    if (e instanceof CouryoError) {
      // e.code é estável: domain_not_verified, daily_limit_reached, recipient_suppressed...
      console.error(e.code, e.message, e.requestId);
      return res.status(201).json({ usuario: usuario.id, aviso: "conta criada, e-mail não enviado" });
    }
    throw e;
  }
});

O SDK já tenta de novo erros de rede, 5xx e rate_limited, sempre com a mesma Idempotency-Key. O que chega no catch é o que não adianta repetir, com a mensagem em português.

3. Receber os eventos por webhook#

A assinatura é calculada sobre o corpo exato. Use express.raw nesta rota, nunca express.json:

server.js
app.post("/webhooks/couryo", express.raw({ type: "application/json" }), (req, res) => {
  let evento;
  try {
    evento = verifyWebhook({ secret: process.env.COURYO_WEBHOOK_SECRET, headers: req.headers, body: req.body });
  } catch (e) {
    if (e instanceof WebhookVerificationError) return res.status(400).send("assinatura inválida");
    throw e;
  }

  switch (evento.type) {
    case "delivered":
      break;
    case "bounced":
    case "complained":
      // O Couryo já suprimiu o endereço quando é o caso. Marque no seu banco, se quiser:
      // evento.data.recipient, evento.data.explanation
      break;
  }
  res.sendStatus(204);
});

app.listen(3000);

Registre o express.json() global, se tiver um, depois desta rota, ou deixe-o só nas rotas que precisam (como acima). Responda rápido: qualquer 2xx confirma; trabalho demorado vai para uma fila sua.

4. Lote, templates e agendamento#

server.js
// Até 100 e-mails numa chamada, com resultado por item, na mesma ordem.
const { data } = await couryo.emails.batch(
  clientes.map((c) => ({ from: "Loja <oi@exemplo.com.br>", to: c.email, template: "aviso-entrega", variables: { nome: c.nome } })),
);

// Agendar para daqui a 1 hora (até 30 dias).
await couryo.emails.send({ from: "Loja <oi@exemplo.com.br>", to: "ana@exemplo.com.br", subject: "Lembrete", text: "...", scheduled_at: new Date(Date.now() + 3600_000).toISOString() });

Por SMTP (Nodemailer)#

Se o projeto já usa Nodemailer, troque só o transporte:

mailer.js
import nodemailer from "nodemailer";

export const transport = nodemailer.createTransport({
  host: "smtp.couryo.com",
  port: 587, // STARTTLS; ou 465 com secure: true
  secure: false,
  requireTLS: true,
  auth: { user: "couryo", pass: process.env.COURYO_API_KEY },
});

Os dois caminhos geram a mesma linha do tempo e os mesmos webhooks. Veja SMTP para os códigos de resposta.