# 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.

Fonte: https://couryo.com/docs/express

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](https://couryo.com/docs/sdks.md).

## 1. Instale

```bash title="Terminal"
npm install express @couryo/node
```

```ini title=".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

```js title="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`:

```js title="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

```js title="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:

```js title="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](https://couryo.com/docs/smtp.md) para os códigos de resposta.
