# RiLiGar Messages: Master Implementation Guide (LLM-Ready) > Este é o guia definitivo para integrar o RiLiGar Messages — e-mail > **transacional** sobre Amazon SES, com API REST, servidor MCP, verificação de > domínio própria, log de envios com linha do tempo de eventos e lista de > supressão por projeto. > **Modelo mental em uma frase:** um *projeto* guarda *domínios*; um domínio só > envia depois que o DNS dele é verificado; cada *envio* acumula *eventos* que > contam o que aconteceu com a mensagem; devolução permanente e reclamação > alimentam sozinhas a *supressão* daquele projeto. > ⚠️ **Leia a seção 8 antes de prometer qualquer coisa ao usuário.** Este > produto NÃO manda SMS nem WhatsApp, NÃO faz campanha em massa e NÃO isola > reputação de envio entre clientes. O nome sugere mais do que o código faz. **Base da API:** `https://messages.worker.riligar.click` **Runtime:** Cloudflare Workers + D1 + R2, com envio pelo Amazon SES. --- ## 1. Estrutura do Sistema (Architecture) - **Projeto** — a unidade de topo, uma por produto seu. Tem `publicKey`, `secretKey` (`sk_…`) e um *configuration set* próprio no SES. Pertence a um dono, identificado pelo `sub` do JWT do RiLiGar Auth. - **Domínio** — o remetente. Cadastrado no projeto, verificado por DNS, e sem ele nenhum envio passa. Um domínio pertence a um projeto só. - **Envio (email)** — uma mensagem enviada, com `id` nosso e `messageId` do SES. É o `messageId` que amarra os eventos que chegam depois. - **Evento** — uma linha do tempo por envio: `sent`, `delivered`, `opened`, `clicked`, `bounced`, `complained`. - **Supressão** — endereço bloqueado NESTE projeto. Devolução permanente e reclamação de spam entram sozinhas; envio para um deles é recusado antes de chegar à AWS. - **Template** — assunto e corpo com `{{variaveis}}`, guardados por nome dentro do projeto. - **API key (`rm_…`)** — credencial revogável do projeto, com permissão `read`, `send` ou `full`. ## 2. Conexão e Autenticação (User Access) Existem **três credenciais**, e a escolha não é de estilo: | Credencial | Identifica | Onde usar | Escopos MCP | | --- | --- | --- | --- | | **JWT do RiLiGar Auth** | uma **PESSOA** (claim `sub`) | painel, e o único caminho para `messages:admin` | os três | | **`sk_…`** (secretKey do projeto) | um **PROJETO** | servidor↔servidor, CI | `read` + `write` | | **`rm_…`** (API key) | um **PROJETO**, revogável | um serviço específico que você pode cortar sozinho | `read` + `write` | Todas vão no mesmo lugar: ```bash Authorization: Bearer sk_sua_chave ``` **A `secretKey` aparece uma vez, na criação do projeto.** A API key também: a resposta traz o valor uma única vez, e depois só o hash existe do nosso lado. Nem a REST nem o MCP devolvem o valor de uma chave já criada. **Permissão da API key** — `read` só lê; `send` lê e envia; `full` também libera endereço da supressão. Uma credencial `read` recebe `403` ao tentar enviar. ## 3. Verificação de domínio (o primeiro passo, e o único que depende de você) ```bash curl -X POST https://messages.worker.riligar.click/projects//domains \ -H "Authorization: Bearer sk_…" \ -H "Content-Type: application/json" \ -d '{"domain":"suaempresa.com.br"}' ``` A resposta traz os registros a criar no DNS: | Tipo | Nome | Papel | | --- | --- | --- | | CNAME ×3 | `._domainkey.` → `.dkim.amazonses.com` | DKIM — assina a mensagem | | TXT | `send.` = `"v=spf1 include:amazonses.com ~all"` | SPF | | MX | `send.` → `feedback-smtp..amazonses.com` (prio 10) | retorno de devolução | | TXT | `_dmarc.` = `"v=DMARC1; p=none;"` | DMARC — **recomendado, não obrigatório** | ⚠️ **O SPF e o MX vão em `send.`, NÃO na raiz.** Se fossem na raiz, sobrescreveriam o SPF que a empresa do usuário já usa e quebrariam o e-mail que ela manda hoje. Não "simplifique" isso apontando para o apex. Depois de criar os registros, confira: ```bash curl -X POST https://messages.worker.riligar.click/projects//domains//verify \ -H "Authorization: Bearer sk_…" ``` A propagação leva de minutos a algumas horas. Enquanto `status` não for `verified`, **nenhum envio com esse remetente passa** — a checagem acontece antes de qualquer chamada à AWS. ## 4. Referência da API (HTTP) ### Envio | Método | Rota | O que faz | | --- | --- | --- | | POST | `/emails` | Envia. `202` com `{ id, messageId, status }`; `200` se foi deduplicado | | GET | `/emails` | Lista os envios do projeto | | GET | `/emails/:id` | Um envio com corpo e a linha do tempo de `events` | | POST | `/attachments?fileName=…` | Sobe um anexo para o R2 e devolve a chave | | GET | `/metrics?days=N` | Contagens e taxas do período. `days` máximo: **90** | Corpo do `POST /emails`: ```json { "from": "oi@suaempresa.com.br", "to": "cliente@exemplo.com", "subject": "Bem-vindo", "html": "

Oi!

", "text": "Oi!", "replyTo": "suporte@suaempresa.com.br", "idempotencyKey": "boas-vindas/usuario-123" } ``` `to` aceita string ou lista. A chave de idempotência pode vir no corpo **ou** no header `Idempotency-Key` — as duas funcionam. ### Domínios, supressão, templates e chaves | Método | Rota | O que faz | | --- | --- | --- | | GET/POST | `/projects` | Lista/cria projeto. **Exige o JWT de uma pessoa**, não uma chave de projeto | | GET | `/projects/:id` | Um projeto | | GET/POST | `/projects/:id/domains` | Lista/cadastra domínio (a criação devolve o DNS) | | DELETE | `/projects/:id/domains/:domainId` | Remove um domínio | | POST | `/projects/:id/domains/:domainId/verify` | Consulta a AWS e atualiza o status | | GET | `/suppressions` | Endereços bloqueados no projeto | | DELETE | `/suppressions/:email` | Libera um endereço. Exige permissão `full` | | GET/POST | `/projects/:id/templates` | Lista/cria template com `{{variaveis}}` | | GET/POST | `/projects/:id/api-keys` | Lista/cria API key. **O valor aparece uma vez só** | | DELETE | `/projects/:id/api-keys/:keyId` | Revoga uma chave | | GET/POST | `/projects/:id/webhooks` | Lista/cadastra endpoint de webhook | | DELETE | `/projects/:id/webhooks/:webhookId` | Remove um endpoint | | GET | `/status` | Saúde pública: `database`, `ses`, `storage` | | POST | `/webhooks/ses` | **Entrada** do SNS/SES. Não é para você chamar | Toda resposta tem a forma `{ message, data }`. ⚠️ **`/emails`, `/suppressions` e `/metrics` nasceram para credencial de PROJETO.** Com o JWT de uma pessoa, diga qual projeto quer ler por `?projectId=` — a posse é conferida do mesmo jeito, e dono errado recebe `404`. ## 5. Idempotência Vale mais neste produto do que em qualquer outro da casa: repetir uma chamada entrega **um segundo e-mail na caixa de uma pessoa real**, e isso não tem desfazer. - Mande `idempotencyKey` (ou o header `Idempotency-Key`) em todo envio. - Repetir com a mesma chave devolve `200` com o envio original e `deduplicated: true`. Nenhum e-mail novo sai. - **Uma tentativa que FALHOU não queima a chave.** Tentar de novo com a mesma chave é o comportamento correto, e o e-mail sai — não há duplicata de algo que não saiu. - Derive a chave do FATO, não do relógio: `boas-vindas/usuario-123`, não `envio-`. - **Pelo MCP a chave é obrigatória.** Na REST é opcional — mas mande sempre. ## 6. Anexos Dois passos, de propósito: sobe o arquivo, depois referencia a chave no envio. ```bash curl -X POST "https://messages.worker.riligar.click/attachments?fileName=nota.pdf" \ -H "Authorization: Bearer sk_…" \ -H "Content-Type: application/pdf" \ --data-binary @nota.pdf ``` ⚠️ **Os tetos são nossos, não do SES:** **6 MB por arquivo** no upload e **8 MB somados por mensagem** — bem abaixo dos 40 MB que a AWS aceita. A razão é concreta: o worker carrega o arquivo inteiro em memória para assinar a chamada à AWS (a assinatura SigV4 exige o corpo completo, e não há streaming nesse caminho). Acima disso, mande um **link**, não um anexo. ## 7. Webhooks (os que saem para você) Você cadastra uma URL no projeto e passa a receber os eventos de entrega. Cada chamada leva: ``` x-riligar-signature: t=,v1= ``` O `v1` é o HMAC-SHA256, com o segredo do endpoint, sobre `.`. **É o mesmo contrato do RiLiGar Payments** — quem integrou um dos dois reaproveita a função de verificação inteira. ```javascript // confira ANTES de ler o corpo const [t, v1] = header.split(',').map(p => p.split('=')[1]) const esperado = hmacSha256(segredo, `${t}.${corpoCru}`) if (esperado !== v1) return new Response('assinatura inválida', { status: 400 }) ``` Tipos de evento: `sent`, `delivered`, `opened`, `clicked`, `bounced`, `complained`. ## 8. Servidor MCP (agentes de IA) **Endpoint:** `https://messages.worker.riligar.click/mcp/` — o id do projeto faz parte do endereço e delimita o alcance do token. **Revisão:** `2026-07-28` (stateless: sem `initialize`, sem sessão, sem stream GET). **Autenticação:** OAuth 2.1 pelo RiLiGar Auth, ou `sk_…`/`rm_…` no header. São **11 ferramentas em 3 escopos**: - `messages:read` — `messages_get_project`, `messages_list_emails`, `messages_get_email`, `messages_get_metrics`, `messages_list_domains`, `messages_list_suppressions` - `messages:write` — `messages_send_email`, `messages_add_domain`, `messages_verify_domain`, `messages_create_template` - `messages:admin` — `messages_remove_suppression` ⚠️ **`messages_send_email` é a ferramenta mais perigosa do grupo RiLiGar.** É a primeira que produz efeito FORA do sistema, numa pessoa que não pediu nada. Por isso: marcada com `destructiveHint`, `idempotencyKey` **obrigatória**, e teto de **200 envios por dia** contados só pelo canal do MCP (a REST não tem esse teto). `messages:admin` só é concedido pelo **JWT**. A `secretKey` e a API key ficam em `read` + `write`. **Documentação integral:** https://messages.riligar.click/llms-mcp.txt ## 9. O que NÃO existe (Anti-Hallucination) Para o agente não inventar produto: - **NÃO manda SMS. NÃO manda WhatsApp.** Só e-mail. Não há provider guardado, esqueleto nem "não implementado" — não existe uma linha disso no código. O nome do produto sugere mais do que ele faz; não complete a lacuna. - **NÃO tem broadcast, audiences, segmentos, contatos nem automações.** Só transacional: a mensagem nasce de um fato do sistema do cliente, não de uma lista. Isso é desenho, não backlog. - **NÃO isola reputação de envio entre clientes.** O configuration set do SES separa as **métricas** de cada projeto; o **pool de IP é compartilhado**, e um cliente com devolução alta degrada a entrega dos outros. IP dedicado não existe aqui. **Nunca prometa "reputação isolada" nem "IP dedicado".** - **NÃO promete que a conta nunca é suspensa.** A AWS avalia o agregado (devolução < 5%, reclamação < 0,1%). O que existe é processo: aviso, motivo escrito e apelação. Prometer leniência é prometer o que ninguém controla. - **NÃO tem envio em lote (`/emails/batch`) nem agendamento (`scheduled_at`).** Um envio por chamada, e ele sai agora. - **NÃO tem e-mail de entrada (inbound).** O MX de `send.` é só o retorno de devolução, não uma caixa de entrada. - **NÃO tem SDK publicado no npm.** No back-end, use a chave contra a API HTTP — são rotas comuns, qualquer linguagem consome. - **NÃO existe rotina que apague o histórico antigo.** O número 90 é o teto da JANELA DE CONSULTA de envios, eventos e métricas — não uma exclusão programada. Diga isso com essas palavras se perguntarem sobre retenção. - **Nenhuma ferramenta ou rota devolve credencial** já criada: nem `secretKey`, nem o valor de uma API key, nem o segredo de um webhook. ## 10. Armadilhas conhecidas (Pitfalls) - **Domínio não verificado = envio recusado.** A checagem é antes da AWS. Se o `from` não tem domínio verificado neste projeto, a rota recusa. É a causa nº1 de "por que meu envio não sai". - **O SPF vai em `send.`, não na raiz.** Apontar para o apex sobrescreve o SPF existente da empresa e quebra o e-mail que ela já manda. - **O DKIM não tolera diferença.** Um caractere a mais no valor do CNAME e a verificação nunca conclui — sem mensagem de erro, só ficando pendente. - **Sem o MX de retorno, a supressão automática não funciona.** É por ele que a devolução volta; sem ele você segue mandando para endereços mortos e a taxa sobe sozinha. - **`/projects` exige o JWT de uma pessoa.** Chave de projeto recebe `403` ali — a chave não pode criar outro projeto. - **`/emails`, `/suppressions` e `/metrics` com JWT precisam de `?projectId=`.** Com `sk_`/`rm_` o parâmetro é ignorado: a credencial já diz de qual projeto ela fala. - **A chave só aparece uma vez.** Vale para a `secretKey` do projeto e para toda API key. Perdeu, cria outra — não há como recuperar. - **Chave de idempotência derivada do relógio não protege nada.** Se ela muda a cada tentativa, a repetição manda outro e-mail. Derive do fato. ## 11. Checklist para IAs (Action Plan) 1. **Criar o projeto** com o JWT do dono (`POST /projects`) e guardar a `secretKey` — ela aparece uma vez só. 2. **Cadastrar o domínio** (`POST /projects/:id/domains`) e criar os 5 registros devolvidos no DNS, com o SPF e o MX em `send.`. 3. **Verificar** (`POST …/verify`) e só seguir quando `status` for `verified`. Antes disso nenhum envio passa. 4. **Enviar** com `POST /emails`, **sempre** com `idempotencyKey` derivada do fato. 5. **Cadastrar o webhook** e verificar `x-riligar-signature` antes de ler o corpo. 6. **Acompanhar `GET /metrics`** — se a devolução passar de 5% ou a reclamação de 0,1%, a conta corre risco de suspensão na AWS. Avise o usuário com o número. 7. **Para operar por agente:** conectar o servidor MCP (seção 8) e começar por `messages_get_project` — é ele que diz se há domínio verificado, e sem isso nada sai. 8. **Não prometa o que não existe:** sem SMS, sem WhatsApp, sem campanha em massa, sem lote, sem agendamento, sem inbound e sem reputação isolada. Se o usuário pedir, diga que este sistema não faz — não sugira um contorno que o código não sustenta. --- **Documentação relacionada:** - https://messages.riligar.click/llms-mcp.txt — o servidor MCP em detalhe - https://riligar.click/llms.txt — a RiLiGar e os outros produtos - https://auth.riligar.click/llms-mcp.txt — o Auth como authorization server