mailcore 0.1.0.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +15 -0
  3. data/CONTRIBUTING.md +22 -0
  4. data/LICENSE +21 -0
  5. data/README.md +185 -0
  6. data/SECURITY.md +28 -0
  7. data/app/controllers/action_mailbox/ingresses/mailcore/inbound_emails_controller.rb +59 -0
  8. data/docs/cobertura.md +140 -0
  9. data/docs/integracao-api-interrupcao.json +27 -0
  10. data/docs/integracao-api.json +48 -0
  11. data/docs/rails.md +185 -0
  12. data/docs/ruby-3.3.13-http-padrao.json +22 -0
  13. data/docs/ruby-3.3.13.json +66 -0
  14. data/docs/ruby-3.4.12-http-padrao.json +22 -0
  15. data/docs/ruby-3.4.12.json +66 -0
  16. data/docs/ruby-4.0.9-http-padrao.json +22 -0
  17. data/docs/ruby-4.0.9.json +66 -0
  18. data/docs/sinatra.md +94 -0
  19. data/docs/verificacao.md +100 -0
  20. data/exemplos/enviar.rb +14 -0
  21. data/exemplos/sinatra.rb +65 -0
  22. data/lib/mailcore/action_mailer.rb +124 -0
  23. data/lib/mailcore/assinatura.rb +64 -0
  24. data/lib/mailcore/cliente.rb +27 -0
  25. data/lib/mailcore/configuracao.rb +30 -0
  26. data/lib/mailcore/engine.rb +71 -0
  27. data/lib/mailcore/erros.rb +25 -0
  28. data/lib/mailcore/limite_ingress.rb +51 -0
  29. data/lib/mailcore/railtie.rb +16 -0
  30. data/lib/mailcore/recursos/automacoes.rb +52 -0
  31. data/lib/mailcore/recursos/base.rb +29 -0
  32. data/lib/mailcore/recursos/caixas.rb +199 -0
  33. data/lib/mailcore/recursos/campanhas.rb +72 -0
  34. data/lib/mailcore/recursos/chaves_api.rb +23 -0
  35. data/lib/mailcore/recursos/cli.rb +24 -0
  36. data/lib/mailcore/recursos/contatos.rb +179 -0
  37. data/lib/mailcore/recursos/dominios.rb +35 -0
  38. data/lib/mailcore/recursos/emails.rb +54 -0
  39. data/lib/mailcore/recursos/eventos.rb +31 -0
  40. data/lib/mailcore/recursos/modelos.rb +106 -0
  41. data/lib/mailcore/recursos/recebimento.rb +54 -0
  42. data/lib/mailcore/recursos/webhooks.rb +57 -0
  43. data/lib/mailcore/resposta.rb +22 -0
  44. data/lib/mailcore/transporte.rb +284 -0
  45. data/lib/mailcore/versao.rb +5 -0
  46. data/lib/mailcore.rb +36 -0
  47. metadata +189 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 48fb7eb69d256776ed0f0fc87263bae696e305d26951713a68b3728e8bd92a2d
4
+ data.tar.gz: 0dc87a1ed77dce322edbe22c410c87644a1a1cde50051ac515b19f85439764fe
5
+ SHA512:
6
+ metadata.gz: 901090c17a27114d7466eacc4a77c78d91ba21f2951331f2c01941b1e003bab4b8f4691095d291358284a3bf23401e175698c03de33144388a3ecd5420876d4d
7
+ data.tar.gz: c5bdefe691e0a3b5be1cc70127dfe3ecc856170b20391eaba9beb2b73684291eb0780fff1c9b6308dea2e1f3c707a8ca0c9428a944efc3c126f13841f0ab38a3
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Alterações
2
+
3
+ ## 0.1.0.alpha.1 — 14/11/2026
4
+
5
+ - Primeira gem Ruby Mailcore, com 151 operações correspondentes ao contrato público
6
+ já integrado pelo SDK Node.
7
+ - Cliente por instância, chave estática ou Proc resolvida por operação, respostas
8
+ com metadados e exceções próprias.
9
+ - TLS verificado, recusa de redirecionamento, prazos totais e limites de resposta,
10
+ com retries implícitos de Net::HTTP desabilitados.
11
+ - Downloads binários e por blocos, HMAC-SHA256 de webhooks e integração Rails
12
+ opcional com Action Mailer e Action Mailbox.
13
+ - Desenvolvimento, matriz de testes e preparação da distribuição em Docker.
14
+
15
+ Esta versão é alpha e não foi publicada no RubyGems.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,22 @@
1
+ # Contribuir
2
+
3
+ Use código, comentários e documentação em português do Brasil, preservando nomes
4
+ exigidos pelos protocolos Ruby/Rails/HTTP. Recursos ficam em `lib/mailcore/recursos`;
5
+ o transporte centraliza autenticação, TLS, erros, prazos e limites.
6
+
7
+ O servidor é a autoridade para os DTOs, escopos, permissões e transições. Compare
8
+ novas operações com as rotas públicas do backend antes de implementá-las. Não
9
+ normalize envelopes diferentes nem transforme ausência de campo em `nil` no JSON.
10
+
11
+ Execute `sh scripts/verificar.sh` com Docker Linux. Não é necessário instalar
12
+ ferramentas no host. Mantenha a matriz de runtimes, lockfile de desenvolvimento,
13
+ testes de contrato/protocolo e instalação da gem coerentes. Rails é opcional:
14
+ carregar o cliente puro não pode exigir Action Mailer ou Action Mailbox.
15
+
16
+ Use dados sintéticos em testes. Não inicie workers/SMTP ou publique portas para
17
+ verificar o cliente. A integração local real usa banco e API exclusivos e remove
18
+ seus recursos. Registre o alcance observado sem atribuir ao laboratório prova de
19
+ entrega, desempenho cloud ou configuração de uma conta de produção.
20
+
21
+ Os comandos de verificação não devem publicar pacotes ou fazer deploy. Uma
22
+ implantação posterior no Mailcore precisa ser testada no ambiente de produção.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mailcore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # Mailcore para Ruby
2
+
3
+ [Código-fonte](https://github.com/mailcore-hq/mailcore-ruby) · [Problemas e sugestões](https://github.com/mailcore-hq/mailcore-ruby/issues)
4
+
5
+ Cliente Ruby da API Mailcore, com integração opcional com Rails. Versão
6
+ alpha `0.1.0.alpha.1`. Requer Ruby 3.3 ou superior.
7
+ Cada instância mantém sua configuração; pedidos concorrentes não compartilham
8
+ credenciais ou estado de envio.
9
+
10
+ ## Instalação
11
+
12
+ Instale a versão alpha pelo RubyGems:
13
+
14
+ ```sh
15
+ gem install mailcore --version 0.1.0.alpha.1 --pre
16
+ ```
17
+
18
+ Com Bundler, adicione ao Gemfile da aplicação:
19
+
20
+ ```ruby
21
+ gem 'mailcore', '0.1.0.alpha.1'
22
+ ```
23
+
24
+ ### Desenvolvimento e instalação local
25
+
26
+ A verificação Docker gera `pkg/mailcore-0.1.0.alpha.1.gem`. Em uma aplicação Ruby:
27
+
28
+ ```sh
29
+ gem install /caminho/mailcore-0.1.0.alpha.1.gem
30
+ ```
31
+
32
+ O comando pode buscar dependências no RubyGems. Outra opção é apontar o Gemfile
33
+ da aplicação para este checkout e executar Bundler:
34
+
35
+ ```ruby
36
+ gem 'mailcore', path: '/caminho/libs-mailcore/mailcore-ruby'
37
+ ```
38
+
39
+ Não é necessário instalar Rails para utilizar o cliente. As dependências de
40
+ runtime são `net-http`, `json`, `openssl`, `base64` e `timeout`; o pacote não
41
+ inclui um cliente HTTP compilado próprio. A faixa JSON 2.x mantém compatibilidade
42
+ com a versão Rails exercitada pelos testes.
43
+
44
+ ## Primeiro envio
45
+
46
+ Configure `MAILCORE_API_KEY` somente no servidor. Use domínio verificado e uma
47
+ chave autorizada para o remetente.
48
+
49
+ ```ruby
50
+ require 'mailcore'
51
+
52
+ cliente = Mailcore::Cliente.new(chave_api: ENV.fetch('MAILCORE_API_KEY'))
53
+ pedido = {
54
+ remetente: 'Equipe <equipe@example.com>',
55
+ destinatarios: ['pessoa@example.com'],
56
+ assunto: 'Bem-vindo',
57
+ html: '<p>Sua conta está pronta.</p>',
58
+ texto: 'Sua conta está pronta.'
59
+ }
60
+ resposta = cliente.emails.enviar(pedido, chave_idempotencia: 'pedido-123-confirmacao')
61
+ puts resposta[:id]
62
+ ```
63
+
64
+ `202` confirma a aceitação da intenção; a entrega é acompanhada pelos eventos da
65
+ mensagem ou webhooks. Reutilize a mesma chave e o mesmo conteúdo ao repetir a
66
+ mesma intenção. A biblioteca não repete requisições automaticamente, inclusive
67
+ após timeout, `429` e desconexão. Um timeout não desfaz uma intenção aceita.
68
+
69
+ Anexos usam `nome`, `tipo_conteudo`, `conteudo_base64` e, no envio composto,
70
+ `id_conteudo` opcional para CID. O lote aceita de 1 a 100 mensagens, em uma única
71
+ operação atômica; não divide pedidos nem aceita anexos, Cc/Cco ou cabeçalhos de
72
+ mensagem. Agendamento usa `agendada_em` ISO 8601 com fuso. As regras de domínio,
73
+ permissões e limites do DTO são validadas pelo servidor.
74
+
75
+ ## Resultados, erros e opções
76
+
77
+ As operações devolvem `Mailcore::Resposta`: `dados` contém o JSON da API com
78
+ chaves simbolizadas recursivamente; `status` e `cabecalhos` preservam metadados
79
+ HTTP. `resposta[:id]` abrevia `resposta.dados[:id]`. Listas mantêm os envelopes
80
+ originais (`dados`, `proximo`, `proximo_cursor`, `tem_mais`, conforme a rota).
81
+ Nenhuma paginação é percorrida automaticamente.
82
+
83
+ ```ruby
84
+ begin
85
+ pagina = cliente.contatos.listar({ limite: 20 })
86
+ proximo = pagina.dados[:proximo_cursor]
87
+ rescue Mailcore::ErroApi => erro
88
+ # erro.codigo, erro.status e erro.cabecalhos['retry-after']
89
+ rescue Mailcore::ErroTransporte => erro
90
+ # Confirme a intenção antes de decidir repetir um envio ambíguo.
91
+ end
92
+ ```
93
+
94
+ `ErroApi` preserva códigos e mensagens JSON públicos do servidor.
95
+ `ErroResposta` identifica JSON inválido e limites excedidos.
96
+ `ErroTransporte` identifica falhas de comunicação, resposta incompleta ou prazo; configuração inválida
97
+ gera `ErroConfiguracao`. As mensagens de transporte não reproduzem credenciais
98
+ ou corpos recebidos. Erros de API podem conter dados privados; registre somente
99
+ os campos necessários.
100
+
101
+ Todas as operações aceitam as palavras-chave `chave_idempotencia`,
102
+ `tempo_limite` (segundos), `limite_resposta_bytes` e `cabecalhos` adicionais.
103
+ Cabeçalhos de autenticação, protocolo e idempotência são reservados pelo cliente.
104
+ O prazo total padrão é 30 segundos, conexão até 10 segundos e resposta até 16 MiB.
105
+ O teto JSON de pedido é 50 MiB, para comportar o envelope de importação CSV;
106
+ cada rota aplica seu limite específico no servidor.
107
+
108
+ ```ruby
109
+ cliente = Mailcore::Cliente.new(
110
+ chave_api: -> { ENV.fetch('MAILCORE_API_KEY') },
111
+ url_base: 'https://mailcore.com.br/v1',
112
+ tempo_limite: 30,
113
+ tempo_conexao: 10
114
+ )
115
+ ```
116
+
117
+ Uma chave em `Proc` é resolvida a cada operação, permitindo rotação. Use instâncias
118
+ distintas para credenciais distintas; configure o módulo global somente no boot.
119
+ O cliente exige HTTPS, verifica certificado e hostname e recusa redirecionamentos.
120
+ Não herda proxies do ambiente. CAs privadas podem ser informadas por
121
+ `certificado_ca: '/caminho/ca.crt'`; TLS permanece verificado. HTTP só é permitido
122
+ em loopback com `permitir_http_local: true`, para testes explícitos.
123
+
124
+ ## Downloads e webhooks
125
+
126
+ Downloads completos devolvem `String` em `Encoding::BINARY`, preservando MIME,
127
+ CSV e anexos. Os métodos com sufixo `_fluxo` exigem bloco e entregam partes de
128
+ forma síncrona, respeitando o ritmo do consumidor e o teto de bytes. A resposta
129
+ final informa o total em `dados`. Prazo e tamanho continuam ativos até terminar.
130
+
131
+ ```ruby
132
+ File.open('/caminho/mensagem.eml.parcial', 'wb') do |arquivo|
133
+ cliente.recebimento.baixar_original_fluxo('id-da-mensagem') do |parte|
134
+ arquivo.write(parte)
135
+ end
136
+ end
137
+ ```
138
+
139
+ Só trate o arquivo como completo depois de sucesso; uma falha pode deixar partes
140
+ já escritas. Downloads usam rotas autenticadas da API; URLs temporárias retornadas
141
+ em metadados não são acessadas automaticamente.
142
+
143
+ ```ruby
144
+ evento = Mailcore.verificar_assinatura(
145
+ corpo_bruto: corpo_recebido_sem_alteracoes,
146
+ cabecalhos: cabecalhos_recebidos,
147
+ segredo: ENV.fetch('MAILCORE_WEBHOOK_SECRET')
148
+ )
149
+ ```
150
+
151
+ A verificação usa HMAC-SHA256, cabeçalhos `svix-*`, comparação em tempo constante
152
+ e janela temporal de cinco minutos. Corpo alterado, cabeçalhos ambíguos e
153
+ timestamps vencidos/futuros são recusados. Deduplicação dos eventos e confirmação
154
+ durável pertencem ao consumidor; uma assinatura válida não torna a ação idempotente.
155
+
156
+ ## Rails e demais recursos
157
+
158
+ O [guia Rails](docs/rails.md) cobre Action Mailer e Action Mailbox, incluindo
159
+ HTML/texto, anexos/CID e recebimento autenticado. Rails e Mail são opcionais para
160
+ a gem; os adaptadores usam os componentes já instalados na aplicação.
161
+ O [exemplo Sinatra](docs/sinatra.md) demonstra uma rota privada de envio com
162
+ destino fixo, usando o mesmo cliente Ruby e a instalação Sinatra da aplicação.
163
+
164
+ A [cobertura](docs/cobertura.md) lista os recursos e contratos. O cliente inclui
165
+ envio, lote, domínios, chaves, contatos, segmentos, tópicos, propriedades,
166
+ importações/exportações, modelos, campanhas, automações, eventos, webhooks,
167
+ recebimento, caixas e concessões CLI. Não converte métodos privados do dashboard
168
+ em API pública nem cria capacidades ausentes no servidor.
169
+
170
+ ## Desenvolver e verificar com Docker
171
+
172
+ Requisitos do host: shell POSIX e Docker Linux acessível ao usuário.
173
+ Ruby, Bundler, Rails e ferramentas de compilação rodam dentro das imagens:
174
+
175
+ ```sh
176
+ sh scripts/verificar.sh
177
+ ```
178
+
179
+ O comando verifica a matriz Ruby 3.3, 3.4 e 4.0, prepara a gem e confere sua
180
+ instalação. Os testes de protocolo usam dados sintéticos, TLS verificado e nenhuma
181
+ porta publicada. A [verificação](docs/verificacao.md) distingue os testes de
182
+ contrato, HTTPS, Rails e integração com a API real local.
183
+
184
+ Publicação no RubyGems e deploy não são executados por esses comandos.
185
+ Leia [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md) e [LICENSE](LICENSE).
data/SECURITY.md ADDED
@@ -0,0 +1,28 @@
1
+ # Segurança
2
+
3
+ Mantenha as chaves no servidor, em variáveis protegidas ou gerenciador de segredos.
4
+ Nunca as inclua no cliente de navegador, Git, URLs, relatórios ou mensagens de log.
5
+ Use chaves com os escopos e domínios necessários. Revogação e autorização atual
6
+ continuam sendo controladas pela API.
7
+
8
+ HTTPS é obrigatório, com verificação de cadeia/hostname. O SDK não segue
9
+ redirecionamentos, não envia cookies, não herda proxies do ambiente e não
10
+ permite substituir cabeçalhos reservados. A opção HTTP de teste se limita a
11
+ loopback explicitamente autorizado. Uma CA privada não desabilita a verificação TLS.
12
+
13
+ Valide assinaturas sobre o corpo bruto antes de processar eventos. Preserve
14
+ deduplicação durável, autorização da aplicação e limites no servidor HTTP que
15
+ recebe o webhook. Repetições legítimas podem ocorrer dentro da janela de assinatura.
16
+ No Action Mailbox, configure armazenamento protegido e retenção de mensagens.
17
+
18
+ Não registre corpos MIME/JSON, endereços, URLs temporárias ou segredos para
19
+ diagnosticar falhas. A gem não registra requisições automaticamente. `inspect`
20
+ de configuração, cliente, transporte e resposta omite credenciais/conteúdo.
21
+
22
+ Em caso de falha em download por blocos, descarte o arquivo parcial antes de
23
+ usá-lo. Em caso de timeout de envio, consulte a intenção por idempotência antes de
24
+ repetir. Não há repetição automática no transporte; tarefas Rails podem possuir
25
+ sua própria política de repetição, que deve preservar a chave/intenção.
26
+
27
+ Relate vulnerabilidades pelo canal privado de manutenção do projeto, com uma
28
+ reprodução sintética. Não publique credenciais ou dados de clientes em issues.
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailbox
4
+ module Ingresses
5
+ module Mailcore
6
+ class InboundEmailsController < ::ActionMailbox::BaseController
7
+ LIMITE_EVENTO_BYTES = ::Mailcore::LimiteIngress::LIMITE_BYTES
8
+ class_attribute :cliente_mailcore, instance_accessor: false, default: nil
9
+
10
+ def create
11
+ return head :content_too_large if request.content_length.to_i > LIMITE_EVENTO_BYTES
12
+
13
+ corpo = request.body.read(LIMITE_EVENTO_BYTES + 1).to_s
14
+ return head :content_too_large if corpo.bytesize > LIMITE_EVENTO_BYTES
15
+
16
+ segredo = segredo_assinatura
17
+ return head :service_unavailable unless segredo.is_a?(String) && !segredo.empty?
18
+
19
+ evento = ::Mailcore.verificar_assinatura(
20
+ corpo_bruto: corpo,
21
+ cabecalhos: {
22
+ 'svix-id' => request.headers['svix-id'],
23
+ 'svix-timestamp' => request.headers['svix-timestamp'],
24
+ 'svix-signature' => request.headers['svix-signature']
25
+ },
26
+ segredo: segredo
27
+ )
28
+ return head :no_content unless evento[:type] == 'email.received'
29
+
30
+ dados = evento[:data]
31
+ unless dados.is_a?(Hash) && dados[:email_id].is_a?(String) && !dados[:email_id].empty?
32
+ return head :unprocessable_entity
33
+ end
34
+
35
+ fonte = ::Mailcore::MensagemActionMailbox.new(
36
+ email_id: dados[:email_id], destinatarios: dados[:to],
37
+ cliente: self.class.cliente_mailcore || ::Mailcore.cliente
38
+ ).original
39
+ ::ActionMailbox::InboundEmail.create_and_extract_message_id!(fonte)
40
+ head :no_content
41
+ rescue ::Mailcore::ErroAssinatura
42
+ head :unauthorized
43
+ rescue ::Mailcore::ErroEnvelopeActionMailbox
44
+ head :unprocessable_entity
45
+ rescue ::Mailcore::Erro
46
+ head :service_unavailable
47
+ end
48
+
49
+ private
50
+
51
+ def segredo_assinatura
52
+ segredo = Rails.application.credentials.dig(:action_mailbox, :mailcore_segredo) ||
53
+ ::Mailcore.configuracao.segredo_webhook
54
+ segredo.respond_to?(:call) ? segredo.call : segredo
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
data/docs/cobertura.md ADDED
@@ -0,0 +1,140 @@
1
+ # Cobertura da gem Mailcore
2
+
3
+ A versão `0.1.0.alpha.1` reúne **16 recursos principais, 30 classes de recursos e
4
+ 151 métodos HTTP**. O contrato foi conferido no backend
5
+ `e001da831365c536f5764dc703fb7a03964f807f` e no SDK Node
6
+ `a234dc2d6f22389f8a57046a142d82a4a4feddf5`, em 14/11/2026. A superfície Ruby
7
+ preserva os caminhos, campos e envelopes selecionados nessa conferência.
8
+
9
+ Os nomes abaixo partem de uma instância `cliente = Mailcore::Cliente.new(...)`.
10
+ Pedidos e filtros são `Hash` posicionais; opções da operação são palavras-chave,
11
+ como `chave_idempotencia:`, `tempo_limite:`, `limite_resposta_bytes:` e
12
+ `cabecalhos:`. Os prazos usam segundos. Os métodos síncronos retornam
13
+ `Mailcore::Resposta`, com `dados`, `status` e `cabecalhos`; `resposta[:id]`
14
+ consulta a chave correspondente em `dados`.
15
+
16
+ ## Recursos e métodos
17
+
18
+ | Recurso | Métodos |
19
+ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `emails` | `enviar`, `listar`, `obter`, `reagendar`, `cancelar`, `eventos`, `obter_por_idempotencia`, `baixar_anexo` |
21
+ | `lotes` | `enviar`, `obter_por_idempotencia` |
22
+ | `dominios` | `criar`, `listar`, `obter`, `atualizar`, `registros_dns`, `verificar`, `remover` |
23
+ | `chaves_api` | `criar`, `listar`, `renomear`, `revogar` |
24
+ | `contatos` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `adicionar_em_lote`, `alterar_em_lote` |
25
+ | `contatos.segmentos` | `listar`, `adicionar`, `remover` |
26
+ | `contatos.topicos` | `listar`, `atualizar` |
27
+ | `contatos.importacoes` | `prever`, `criar`, `obter` |
28
+ | `contatos.exportacoes` | `criar`, `listar`, `obter`, `baixar`, `baixar_fluxo` |
29
+ | `segmentos` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
30
+ | `topicos` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
31
+ | `propriedades_contatos` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
32
+ | `modelos` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `publicar`, `duplicar`, `prever`, `testar`, `obter_previa` |
33
+ | `modelos.versoes` | `listar`, `obter`, `restaurar`, `prever` |
34
+ | `modelos.pastas` | `listar`, `criar`, `atualizar`, `remover` |
35
+ | `webhooks` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
36
+ | `webhooks.eventos` | `listar`, `obter`, `listar_tentativas`, `reenviar` |
37
+ | `campanhas` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `duplicar`, `revisar`, `enviar`, `cancelar`, `prever`, `ler_previa`, `testar`, `obter_metricas`, `listar_destinatarios`, `listar_links_clicados` |
38
+ | `automacoes` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `duplicar`, `parar` |
39
+ | `automacoes.execucoes` | `listar`, `obter` |
40
+ | `eventos` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `enviar` |
41
+ | `recebimento` | `listar`, `obter`, `listar_anexos`, `obter_anexo`, `baixar_original`, `baixar_anexo`, `baixar_original_fluxo`, `baixar_anexo_fluxo` |
42
+ | `caixas` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
43
+ | `caixas.etiquetas` | `criar`, `listar`, `obter`, `atualizar`, `remover` |
44
+ | `caixas.conversas` | `listar`, `buscar`, `obter`, `atualizar`, `remover` |
45
+ | `caixas.conversas.etiquetas` | `adicionar`, `substituir`, `remover` |
46
+ | `caixas.conversas.emails` | `listar`, `obter`, `responder`, `encaminhar` |
47
+ | `caixas.rascunhos` | `criar`, `listar`, `obter`, `atualizar`, `remover`, `enviar` |
48
+ | `cli.concessoes` | `listar`, `revogar` |
49
+
50
+ `cli` é a classe agregadora que expõe `concessoes`, completando as 30 classes.
51
+ A função local `Mailcore.verificar_assinatura` verifica webhooks sem fazer
52
+ requisições HTTP e não integra a contagem dos 151 métodos.
53
+
54
+ ## Pedidos, respostas e paginação
55
+
56
+ A gem mantém os nomes em português do contrato HTTP. Campos como
57
+ `remetente`, `destinatarios`, `agendada_em`, `segmento_id` e `revisao` são enviados
58
+ conforme fornecidos. O servidor valida regras de negócio, permissões e os DTOs
59
+ específicos de cada recurso. A gem não converte um pedido de caixa em envio
60
+ transacional nem acrescenta campos para ampliar permissões.
61
+
62
+ JSON admite strings e símbolos como chaves, texto codificável em UTF-8, inteiros,
63
+ floats finitos, booleanos, arrays, hashes e `nil`. Objetos arbitrários e números
64
+ que seriam serializados como texto são recusados. Nomes de chave precisam
65
+ permanecer únicos após a conversão para UTF-8. Respostas JSON usam chaves
66
+ simbolizadas, preservando arrays e valores anuláveis.
67
+
68
+ Omissão de um campo e seu envio explícito com `nil` têm significados diferentes.
69
+ O transporte conserva `nil` e `false` no corpo; não aplica `compact`. Nos filtros
70
+ da URL, `nil` é omitido e `false` permanece como `false`. Identificadores são
71
+ codificados como um segmento de caminho; UUIDs, aliases, nomes de eventos e
72
+ endereços de contato não viram trechos de URL controlados pela aplicação.
73
+
74
+ A paginação é explícita. O cliente consulta uma página por chamada e devolve os
75
+ cursores sem recalcular ou carregar páginas adicionais. O servidor pode aplicar
76
+ limites diferentes por recurso; os envelopes permanecem próprios:
77
+
78
+ | Envelope dentro de `resposta.dados` | Grupos |
79
+ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
80
+ | `{ dados:, proximo: }` | E-mails, domínios e concessões CLI |
81
+ | `{ dados:, tem_mais:, proximo_cursor: }` | Contatos, exportações, segmentos, tópicos, propriedades de contatos, modelos/versões, webhooks/eventos/tentativas |
82
+ | `{ dados:, proximo:, tem_mais: }` | Campanhas e suas listas, automações/execuções, eventos personalizados, recebimento e caixas |
83
+ | `{ dados: }` | Chaves de API, pastas de modelos e eventos de entrega de um e-mail |
84
+
85
+ ## Contratos por área
86
+
87
+ | Área | Regras relevantes |
88
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
89
+ | Envio transacional | Texto e/ou HTML, Cc/Cco, cabeçalhos e anexos CID conforme o DTO de composição. Até 50 destinatários somados, HTML + texto até 500 KiB, JSON do endpoint até 8 MiB, até 10 anexos com soma decodificada de 5 MiB nos tipos aceitos. A gem não baixa anexos remotos do envio transacional. Aceitação retorna `id` e `estado`; a entrega é acompanhada separadamente. |
90
+ | Envio por modelo | Modelo publicado por ID/alias e variáveis. Usa o DTO específico, com anexos básicos e sem compor simultaneamente conteúdo direto, Cc/Cco, cabeçalhos ou CID. |
91
+ | Lotes | Array de 1 a 100 mensagens, com aceitação atômica e ordenada. Sem anexos, Cc/Cco ou cabeçalhos de mensagem. Uma operação gera uma chamada; a gem não divide o lote ou repete automaticamente seu envio. |
92
+ | Agendamento | `agendada_em` usa ISO 8601 com fuso, dentro da janela de até 30 dias. Reagendar e cancelar dependem do estado atual e da autorização da chave. |
93
+ | Domínios | Capacidades de envio/recebimento `enabled` ou `disabled`, mantendo ao menos uma habilitada. Tracking e diagnóstico seguem os campos do servidor. Remover devolve o estado suspenso do domínio. |
94
+ | Chaves de API | O segredo aparece somente na criação. A alteração pública renomeia a chave; a revogação devolve resposta sem conteúdo. A listagem não usa cursor. |
95
+ | Contatos | CRUD por UUID ou e-mail; associações exigem UUID. Nomes anuláveis, propriedades tipadas e preferências são preservados. Operações em lote admitem até 1.000 contatos. Tópicos usam as assinaturas `opt_in` e `opt_out`. |
96
+ | Importação de contatos | CSV UTF-8 em string JSON, até 8 MiB e 100.000 registros, com mapeamento por índice de coluna. O upload admite JSON até 50 MiB para acomodar os escapes. `prever` não persiste; `criar` inicia processamento e `obter` acompanha o estado. Não há método de listagem de importações. |
97
+ | Exportação de contatos | Criar com filtros, consultar/listar e baixar CSV. Criação e download revalidam o papel administrativo. `baixar` devolve bytes; `baixar_fluxo` entrega os blocos ao consumidor e devolve a contagem total. |
98
+ | Modelos | CRUD e publicação por UUID/alias, com revisão exigida na edição, publicação e restauração. Versões imutáveis, pastas hierárquicas, prévias e testes com destinatários explícitos. `obter_previa(organizacao_id, previa_id)` lê HTML pela rota autenticada. |
99
+ | Campanhas | Marketing para segmento, tópico opcional, revisão e confirmação explícita no envio. Testes usam destinatários explícitos. Cancelamento respeita o estado da campanha; despacho concluído não comprova entrega. Métricas mantêm `nil` quando a telemetria está indisponível. |
100
+ | Automações | Fluxos ativos/inativos, etapas e conexões informadas juntas, condições, esperas, intervalos e ações do contrato. Parar impede novas inscrições e preserva as execuções aceitas. A gem consulta execuções; não oferece mutação individual dessas execuções. |
101
+ | Eventos personalizados | Definição com nome imutável e esquema opcional. Enviar ocorrência exige exatamente `email` ou `contato_id`, com `dados` até 64 KiB. Esquema `nil` difere de `{}`. O nome literal `send` continua consultável e editável pelos métodos de definição. |
102
+ | Webhooks | CRUD, eventos, tentativas e reenvio do evento original. O servidor preserva identificador e payload no replay. Consumidores devem verificar a assinatura sobre o corpo bruto e deduplicar o `svix-id` antes de executar efeitos. |
103
+ | Recebimento | Lista, busca, intervalo temporal e metadados/anexos. `obter` aceita `formato_html: 'cid'` ou `'data_uri'` no Hash de filtros; o formato efetivo pode ser `cid` por limite de expansão. Cabeçalhos repetidos permanecem pares ordenados. MIME original e anexos usam rotas fixas autenticadas. |
104
+ | Caixas | Caixas, etiquetas, conversas, mensagens e rascunhos usam IDs explícitos e revisões conforme o DTO. Responder e encaminhar aceitam a intenção de envio pela fila. Remover conversa a move para a lixeira. Etiquetas podem ser substituídas por `[]` para remover todas as associações. |
105
+ | Anexos de caixas | Resposta/encaminhamento admitem Base64 ou URL e CID conforme o contrato; rascunhos recusam CID fornecido pelo cliente. O servidor resolve URLs com suas próprias proteções. O cliente Ruby não navega nessas URLs. JSON de caixas é limitado a 8 MiB no endpoint. |
106
+ | Concessões CLI | Listagem e revogação das concessões autorizadas do ator atual. Fluxos interativos de autorização e renovação permanecem fora do cliente comum. |
107
+
108
+ O teto de **50 MiB do transporte JSON** acomoda a importação escapada; ele não
109
+ substitui limites menores de cada endpoint, como os 8 MiB do envio e das caixas.
110
+ O limite padrão de leitura da resposta é **16 MiB**, configurável por operação.
111
+ Downloads usam `String` binária. Os métodos com sufixo `_fluxo` exigem um bloco,
112
+ leem de forma síncrona e mantêm o prazo e o teto de bytes durante o consumo.
113
+
114
+ ## Recebimento, Rails e limites de superfície
115
+
116
+ O recurso `recebimento` acessa somente as rotas autenticadas de consulta, anexos,
117
+ MIME original e download. URLs temporárias presentes nos metadados são
118
+ capacidades sensíveis; a gem não as segue automaticamente e não envia a chave
119
+ Mailcore a um endereço devolvido pela API. O conteúdo recebido está sujeito à
120
+ retenção e à disponibilidade atuais do servidor. Listagem não garante que MIME
121
+ ou anexos ainda estejam disponíveis.
122
+
123
+ Rails é opcional. As integrações Action Mailer e Action Mailbox usam a mesma gem
124
+ e o mesmo cliente HTTP; o cliente Ruby básico não exige carregar Rails. Templates
125
+ Ruby/Rails podem produzir o HTML e o texto enviados ao Mailcore. A renderização
126
+ React pertence à integração JavaScript e não faz parte desta gem.
127
+
128
+ Esta matriz cobre os 151 métodos selecionados, sem anunciar toda a API do
129
+ produto. As rotas públicas de métricas globais e auditoria existentes no backend
130
+ ficam fora desta versão, assim como no SDK Node usado na conferência. Nenhum
131
+ helper de dashboard, sessão, visualização compartilhada ou token temporário foi
132
+ acrescentado por inferência. Endereços de anexos de caixas com prefixo de dashboard
133
+ continuam como metadados; não há um método público fictício para baixá-los.
134
+
135
+ O teste de contrato exercita os 151 métodos contra um transporte coletor,
136
+ conferindo método, caminho, corpo, filtros, formato e opções. Os testes HTTPS
137
+ reais com servidor sintético cobrem o transporte separadamente. Essa combinação
138
+ não equivale a executar cada endpoint contra uma conta real ou a medir capacidade
139
+ de envio em produção; os resultados efetivamente executados estão no relatório
140
+ de verificação do repositório.
@@ -0,0 +1,27 @@
1
+ {
2
+ "data_utc": "2026-11-14T13:53:31.104091+00:00",
3
+ "resultado": "falhou",
4
+ "ambiente": "api_postgres_tls_exclusivos_sem_worker_sem_portas",
5
+ "instalacao_host": false,
6
+ "rede_interna": true,
7
+ "portas_publicadas": false,
8
+ "worker_executado": false,
9
+ "smtp_executado": false,
10
+ "deploy_executado": false,
11
+ "tls_api_verificado": false,
12
+ "tls_postgres": "nao_verificado",
13
+ "cenarios": [],
14
+ "etapa": "preparar_identidades",
15
+ "imagens": {
16
+ "cliente": "sha256:149a81a02cb846ea058823aabc578bde130e86c93800e6335b2e89ed44e46d22",
17
+ "api_runtime": "sha256:158e95e9b87f2a126c038e6931b2f956fe6120f3566fd047a29250bd7783ff33",
18
+ "compilador": "sha256:8fa55b2f3ddf97471ab6a767bfa3f37e6bad0986ba823e75fea57e2a2a5c3073",
19
+ "postgres": "sha256:f1ea2a1a4fb7b68d4836be1fdcb8a6466a89b072158bf9d97663600538c294c5"
20
+ },
21
+ "backend_revisao": "52992028e5be8888db699d82e3cc8484140d6a67",
22
+ "backend_fontes_sha256": "d31c825268a64c858276827f47d27e506f543421327f487d7165e511359e248f",
23
+ "backend_fontes_alterados": false,
24
+ "api_compilada_neste_ensaio": true,
25
+ "falha": "Ensaio interrompido; recursos exclusivos serão removidos.",
26
+ "limpeza_concluida": true
27
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "data_utc": "2026-11-14T13:51:11.583482+00:00",
3
+ "resultado": "aprovado",
4
+ "ambiente": "api_postgres_tls_exclusivos_sem_worker_sem_portas",
5
+ "instalacao_host": false,
6
+ "rede_interna": true,
7
+ "portas_publicadas": false,
8
+ "worker_executado": false,
9
+ "smtp_executado": false,
10
+ "deploy_executado": false,
11
+ "tls_api_verificado": true,
12
+ "tls_postgres": "verify-full",
13
+ "cenarios": [
14
+ "chaves_full_sending_escopo_inspect",
15
+ "envio_composto_idempotencia_409_download_cancelamento",
16
+ "lote_atomico_repeticao_conflito",
17
+ "dominios_capacidades_tracking_crud",
18
+ "contatos_nulos_catalogos_exportacao_csv_fluxo",
19
+ "modelos_pastas_versoes_previa_publicacao_cas",
20
+ "marketing_crud_sem_disparo",
21
+ "caixas_etiquetas_rascunhos_cas_sem_envio",
22
+ "recebimento_mime_real_pendente_bytes_fluxo",
23
+ "cli_revogacao_e_limpeza_crud"
24
+ ],
25
+ "etapa": "concluido",
26
+ "imagens": {
27
+ "cliente": "sha256:1b334f043e1073752505db50e5d893d4c691a6d5478125ad5eee0038af2a2967",
28
+ "api_runtime": "sha256:158e95e9b87f2a126c038e6931b2f956fe6120f3566fd047a29250bd7783ff33",
29
+ "compilador": "sha256:8fa55b2f3ddf97471ab6a767bfa3f37e6bad0986ba823e75fea57e2a2a5c3073",
30
+ "postgres": "sha256:f1ea2a1a4fb7b68d4836be1fdcb8a6466a89b072158bf9d97663600538c294c5"
31
+ },
32
+ "backend_revisao": "83c2fa9859e03473e7af56eede97c6a1cad8592c",
33
+ "backend_fontes_sha256": "d31c825268a64c858276827f47d27e506f543421327f487d7165e511359e248f",
34
+ "backend_fontes_alterados": false,
35
+ "api_compilada_neste_ensaio": true,
36
+ "api_binario_sha256": "3cd977ab90dbfcc0067b7d0b85d120ae9132f4e873cc223298f901fc1dbd35bf",
37
+ "migracoes_aplicadas": 23,
38
+ "ruby": "3.4.12",
39
+ "recebimento_processado_por_worker": false,
40
+ "rails_real_exercitado": false,
41
+ "persistencia_sintetica": {
42
+ "mensagens": 3,
43
+ "tarefas_envio": 3,
44
+ "recebidos": 1,
45
+ "recebidos_pendentes": 1
46
+ },
47
+ "limpeza_concluida": true
48
+ }