mailcore-sdk 0.1.0a1__tar.gz

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 (50) hide show
  1. mailcore_sdk-0.1.0a1/CHANGELOG.md +25 -0
  2. mailcore_sdk-0.1.0a1/CONTRIBUTING.md +46 -0
  3. mailcore_sdk-0.1.0a1/LICENSE +21 -0
  4. mailcore_sdk-0.1.0a1/MANIFEST.in +9 -0
  5. mailcore_sdk-0.1.0a1/PKG-INFO +142 -0
  6. mailcore_sdk-0.1.0a1/README.md +118 -0
  7. mailcore_sdk-0.1.0a1/SECURITY.md +43 -0
  8. mailcore_sdk-0.1.0a1/docs/cobertura.md +148 -0
  9. mailcore_sdk-0.1.0a1/docs/integracao-api-interrupcao.json +27 -0
  10. mailcore_sdk-0.1.0a1/docs/integracao-api.json +52 -0
  11. mailcore_sdk-0.1.0a1/docs/integracoes.md +145 -0
  12. mailcore_sdk-0.1.0a1/docs/python-3.11.18.json +22 -0
  13. mailcore_sdk-0.1.0a1/docs/python-3.12.16.json +22 -0
  14. mailcore_sdk-0.1.0a1/docs/python-3.13.18.json +22 -0
  15. mailcore_sdk-0.1.0a1/docs/python-3.14.10.json +22 -0
  16. mailcore_sdk-0.1.0a1/docs/python-3.15.2.json +22 -0
  17. mailcore_sdk-0.1.0a1/docs/verificacao.md +81 -0
  18. mailcore_sdk-0.1.0a1/exemplos/compartilhado.py +130 -0
  19. mailcore_sdk-0.1.0a1/exemplos/django_app.py +65 -0
  20. mailcore_sdk-0.1.0a1/exemplos/fastapi_app.py +69 -0
  21. mailcore_sdk-0.1.0a1/exemplos/flask_app.py +61 -0
  22. mailcore_sdk-0.1.0a1/pyproject.toml +51 -0
  23. mailcore_sdk-0.1.0a1/setup.cfg +4 -0
  24. mailcore_sdk-0.1.0a1/src/mailcore/__init__.py +47 -0
  25. mailcore_sdk-0.1.0a1/src/mailcore/assinatura.py +105 -0
  26. mailcore_sdk-0.1.0a1/src/mailcore/cliente.py +105 -0
  27. mailcore_sdk-0.1.0a1/src/mailcore/erros.py +42 -0
  28. mailcore_sdk-0.1.0a1/src/mailcore/py.typed +0 -0
  29. mailcore_sdk-0.1.0a1/src/mailcore/recursos/__init__.py +93 -0
  30. mailcore_sdk-0.1.0a1/src/mailcore/recursos/automacoes.py +252 -0
  31. mailcore_sdk-0.1.0a1/src/mailcore/recursos/base.py +51 -0
  32. mailcore_sdk-0.1.0a1/src/mailcore/recursos/caixas.py +784 -0
  33. mailcore_sdk-0.1.0a1/src/mailcore/recursos/campanhas.py +391 -0
  34. mailcore_sdk-0.1.0a1/src/mailcore/recursos/chaves_api.py +109 -0
  35. mailcore_sdk-0.1.0a1/src/mailcore/recursos/cli.py +78 -0
  36. mailcore_sdk-0.1.0a1/src/mailcore/recursos/contatos.py +892 -0
  37. mailcore_sdk-0.1.0a1/src/mailcore/recursos/dominios.py +179 -0
  38. mailcore_sdk-0.1.0a1/src/mailcore/recursos/emails.py +266 -0
  39. mailcore_sdk-0.1.0a1/src/mailcore/recursos/eventos.py +159 -0
  40. mailcore_sdk-0.1.0a1/src/mailcore/recursos/modelos.py +484 -0
  41. mailcore_sdk-0.1.0a1/src/mailcore/recursos/recebimento.py +211 -0
  42. mailcore_sdk-0.1.0a1/src/mailcore/recursos/webhooks.py +252 -0
  43. mailcore_sdk-0.1.0a1/src/mailcore/tipos.py +164 -0
  44. mailcore_sdk-0.1.0a1/src/mailcore/transporte.py +602 -0
  45. mailcore_sdk-0.1.0a1/src/mailcore/versao.py +3 -0
  46. mailcore_sdk-0.1.0a1/src/mailcore_sdk.egg-info/PKG-INFO +142 -0
  47. mailcore_sdk-0.1.0a1/src/mailcore_sdk.egg-info/SOURCES.txt +48 -0
  48. mailcore_sdk-0.1.0a1/src/mailcore_sdk.egg-info/dependency_links.txt +1 -0
  49. mailcore_sdk-0.1.0a1/src/mailcore_sdk.egg-info/requires.txt +2 -0
  50. mailcore_sdk-0.1.0a1/src/mailcore_sdk.egg-info/top_level.txt +1 -0
@@ -0,0 +1,25 @@
1
+ # Histórico de alterações
2
+
3
+ ## 0.1.0a1 — 14/11/2026
4
+
5
+ Primeira versão alpha local da distribuição `mailcore-sdk`, importada como
6
+ `mailcore`. Ainda não publicada no PyPI.
7
+
8
+ - Clientes síncrono e assíncrono com estado e pools próprios, fechamento explícito
9
+ e suporte a `with`/`async with`.
10
+ - Dezesseis recursos principais e 151 operações por variante, com campos e nomes
11
+ PT-BR, preservando o contrato HTTP Mailcore.
12
+ - Tipos de envio, lote, aceitação e configuração, `Resposta[T]` e marcador PEP 561.
13
+ Entidades complexas continuam parcialmente tipadas como `Resposta[Any]`.
14
+ - Downloads autenticados, bytes e fluxos limitados; verificação local de
15
+ assinatura sobre o corpo bruto dos webhooks.
16
+ - HTTPS verificado, redirecionamentos recusados, ausência de cookies/proxies do
17
+ ambiente, respostas limitadas e nenhuma repetição automática.
18
+ - Exemplos opcionais Flask, FastAPI e Django com token de aplicação separado,
19
+ envelope fixo, idempotência externa e leitura limitada.
20
+ - Verificação em Docker para a matriz CPython 3.11–3.15, testes de contrato,
21
+ transporte HTTPS e API local isolada, sem publicação ou entrega SMTP.
22
+
23
+ `tempo_limite` segue a inatividade de cada etapa HTTPX. Ele não representa prazo
24
+ total de relógio. Consulte [a cobertura](docs/cobertura.md) para os limites e o
25
+ alcance desta versão.
@@ -0,0 +1,46 @@
1
+ # Contribuir
2
+
3
+ O projeto está em alpha e distribui `mailcore-sdk`, importado como `mailcore`.
4
+ Mantenha código e documentação em português do Brasil e preserve os nomes e
5
+ envelopes do contrato público da API Mailcore.
6
+
7
+ ## Implementação
8
+
9
+ - Confirme a rota, o DTO, o escopo de autorização e a resposta no backend antes
10
+ de acrescentar uma operação. Capacidade de API não comprova uma tela ou fluxo.
11
+ - Mantenha paridade entre `Cliente` e `ClienteAsync`, sem estado global de
12
+ credenciais. Reutilize o transporte e seus pools, em vez de criar clientes a
13
+ cada pedido.
14
+ - Preserve omissão, `None`, `False`, revisões e envelopes de paginação. Não
15
+ traduza valores do protocolo nem normalize DTOs diferentes para um só formato.
16
+ - Atualize os tipos públicos e a cobertura sem anunciar TypedDicts completos
17
+ quando a resposta ainda for `Resposta[Any]`.
18
+ - Recursos gerados são mantidos pela especificação de
19
+ `scripts/gerar_recursos.py`. Alterações nesse contrato devem atualizar a fonte
20
+ e as duas variantes, com teste de caminho, query, corpo e retorno.
21
+
22
+ Os exemplos dos frameworks ficam separados do pacote principal. O SDK básico não
23
+ deve importar Flask, FastAPI ou Django. Mantenha testes de autenticação,
24
+ idempotência, limites e sanitização ao alterar esses exemplos.
25
+
26
+ ## Verificação
27
+
28
+ Use o verificador Docker do projeto para executar formatação, lint, tipagem,
29
+ testes e empacotamento, sem instalar dependências no host. A matriz é CPython
30
+ 3.11–3.15; o conjunto de frameworks respeita a compatibilidade de cada versão.
31
+ Consulte os scripts e relatórios locais para os comandos e as imagens do ensaio.
32
+
33
+ Testes que iniciam serviços devem usar rede Docker interna ou loopback do próprio
34
+ container, dados sintéticos exclusivos, nenhuma porta publicada e limpeza dos
35
+ recursos criados. Não use produção, tokens pessoais, SMTP ou contas reais nos
36
+ testes do SDK. Use CA de teste confiada explicitamente; não desative a verificação
37
+ TLS. Não publique pacotes ou aplicações durante a verificação local.
38
+
39
+ Inclua verificações pertinentes à mudança: contrato e tipos, HTTP real quando o
40
+ transporte mudar, fechamento de fluxos, cancelamento, erros sem repetição e
41
+ ausência de segredos nas respostas. Diferencie resultados de um transporte falso,
42
+ HTTPS sintético, API local e serviço de produção.
43
+
44
+ Descreva a mudança, sua motivação, as validações executadas e limites conhecidos.
45
+ Atualize `CHANGELOG.md` quando houver alteração pública. Relate problemas de
46
+ segurança conforme [SECURITY.md](SECURITY.md).
@@ -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.
@@ -0,0 +1,9 @@
1
+ include LICENSE README.md CHANGELOG.md SECURITY.md CONTRIBUTING.md pyproject.toml
2
+ recursive-include src/mailcore *.py py.typed
3
+ recursive-include docs *.md *.json
4
+ recursive-include exemplos *.py
5
+ prune testes
6
+ prune scripts
7
+ prune build
8
+ prune .git
9
+ global-exclude __pycache__ *.py[cod] .env .env.*
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: mailcore-sdk
3
+ Version: 0.1.0a1
4
+ Summary: Cliente Python da API Mailcore com recursos síncronos e assíncronos.
5
+ Author: Mailcore
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://mailcore.com.br
8
+ Project-URL: Repository, https://github.com/mailcore-hq/mailcore-python
9
+ Project-URL: Issues, https://github.com/mailcore-hq/mailcore-python/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: 3.15
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: httpx<0.29,>=0.28.1
22
+ Requires-Dist: anyio<5,>=4.8
23
+ Dynamic: license-file
24
+
25
+ # Mailcore para Python
26
+
27
+ [Código-fonte](https://github.com/mailcore-hq/mailcore-python) · [Problemas e sugestões](https://github.com/mailcore-hq/mailcore-python/issues)
28
+
29
+ Cliente Python da API Mailcore, em versão `0.1.0a1`. O pacote de distribuição é
30
+ `mailcore-sdk` e o módulo importado é `mailcore`. Esta versão ainda não foi
31
+ publicada no PyPI. Requer Python 3.11 ou superior.
32
+
33
+ ## Instalação local e primeiro envio
34
+
35
+ Depois da verificação Docker, instale o arquivo local em sua aplicação Python:
36
+
37
+ ```sh
38
+ python -m pip install /caminho/mailcore_sdk-0.1.0a1-py3-none-any.whl
39
+ ```
40
+
41
+ O pip pode buscar as dependências no PyPI; o arquivo do próprio SDK é local.
42
+ Também é possível instalar o `.tar.gz` de `dist/`, ou o checkout em modo editável
43
+ durante o desenvolvimento da sua aplicação.
44
+
45
+ ```python
46
+ import os
47
+ from mailcore import Cliente, PedidoEmail
48
+
49
+ pedido: PedidoEmail = {
50
+ "remetente": "Equipe <equipe@example.com>",
51
+ "destinatarios": ["pessoa@example.com"],
52
+ "assunto": "Bem-vindo",
53
+ "html": "<p>Sua conta está pronta.</p>",
54
+ }
55
+ with Cliente(chave_api=os.environ["MAILCORE_API_KEY"]) as cliente:
56
+ resposta = cliente.emails.enviar(pedido, chave_idempotencia="pedido-123-confirmacao")
57
+ print(resposta["id"])
58
+ ```
59
+
60
+ Use a chave somente no servidor, com remetente de domínio verificado. A aceitação
61
+ da intenção não comprova entrega SMTP. Preserve chave e conteúdo ao repetir uma
62
+ intenção; o SDK não faz tentativas automáticas após erro ou timeout.
63
+
64
+ ## Cliente assíncrono
65
+
66
+ ```python
67
+ from mailcore import ClienteAsync, PedidoEmail
68
+
69
+ async def enviar(pedido: PedidoEmail) -> str:
70
+ async with ClienteAsync() as cliente:
71
+ resposta = await cliente.emails.enviar(pedido, chave_idempotencia="pedido-123-confirmacao")
72
+ return resposta.dados["id"]
73
+ ```
74
+
75
+ Os dois clientes oferecem os mesmos recursos. As chamadas async usam HTTPX
76
+ assíncrono nativo. Reutilize o cliente durante a vida da aplicação; `with`,
77
+ `async with` ou `fechar()` liberam os pools. Não compartilhe um cliente async
78
+ entre event loops. A chave pode ser uma função sem argumentos, resolvida por
79
+ operação, para permitir rotação sem alterar um estado global.
80
+ Um fornecedor que retorne uma chave inválida falha; não usa outra chave do ambiente.
81
+ O pool padrão permite até 100 conexões e 20 ociosas, configuráveis por
82
+ `maximo_conexoes` e `conexoes_ociosas`.
83
+
84
+ ## Respostas e erros
85
+
86
+ `Resposta.dados` preserva os campos e envelopes JSON da API. `status` e
87
+ `cabecalhos` expõem metadados HTTP, e `resposta["id"]` abrevia o acesso ao JSON.
88
+ Bytes de downloads são `bytes`; prévias HTML são `str`. `ErroApi` preserva código
89
+ e status da rejeição; `ErroTransporte` trata rede/prazo; `ErroResposta` trata
90
+ conteúdo inválido ou excedido; `ErroConfiguracao` trata opções inválidas.
91
+
92
+ O cliente exige TLS verificado, não segue redirecionamentos, não armazena cookies
93
+ e não herda proxies do ambiente. Uma CA privada pode ser informada por
94
+ `certificado_ca`. Somente loopback pode usar HTTP com `permitir_http_local=True`.
95
+ `transporte_http` permite um adaptador HTTPX explicitamente fornecido pela aplicação;
96
+ esse adaptador e sua política de rede são responsabilidade de quem o configura.
97
+
98
+ `tempo_limite` (30 segundos por padrão) segue a semântica HTTPX: limite de
99
+ inatividade de cada etapa de conexão, pool, escrita ou leitura. **Não é um prazo
100
+ total de relógio**. A conexão tem limite adicional de 10 segundos. Uma aplicação
101
+ async que precise de prazo total pode envolver a operação em `asyncio.timeout()`;
102
+ o cancelamento é propagado e a resposta é fechada. Um prazo vencido não desfaz
103
+ uma intenção já aceita.
104
+
105
+ A resposta tem teto padrão de 16 MiB, inclusive em fluxo, ajustável por operação
106
+ com `limite_resposta_bytes`. O JSON de pedido admite até 50 MiB para o envelope de
107
+ importação CSV; os limites específicos de cada DTO pertencem à API. A comunicação
108
+ solicita `Accept-Encoding: identity` e recusa codificações não negociadas.
109
+
110
+ ## Downloads e webhooks
111
+
112
+ ```python
113
+ with Cliente() as cliente:
114
+ with cliente.recebimento.baixar_original_fluxo("id-da-mensagem") as fluxo:
115
+ with open("mensagem.eml.parcial", "wb") as arquivo:
116
+ for parte in fluxo:
117
+ arquivo.write(parte)
118
+ ```
119
+
120
+ No cliente async, use `async with` e `async for`. Os fluxos preservam bytes MIME,
121
+ exigem consumo dentro do contexto e liberam a conexão ao terminar, interromper
122
+ ou cancelar. Só trate arquivos parciais como completos depois de sucesso.
123
+ Downloads usam rotas autenticadas; URLs temporárias de metadados não são acessadas
124
+ automaticamente.
125
+
126
+ `verificar_assinatura(corpo_bruto, cabecalhos, segredo)` verifica HMAC-SHA256
127
+ `svix-*`, com comparação em tempo constante e janela de cinco minutos. Passe o
128
+ corpo bruto antes de interpretar JSON. Deduplicação e confirmação durável dos
129
+ eventos pertencem ao consumidor.
130
+
131
+ ## Documentação e desenvolvimento
132
+
133
+ Consulte [cobertura](docs/cobertura.md), [integrações Flask/FastAPI/Django](docs/integracoes.md)
134
+ e [verificação](docs/verificacao.md). Não há dependência de framework no runtime
135
+ do pacote. Instalação local usa os arquivos `.whl`/`.tar.gz` preparados em `dist/`.
136
+
137
+ ```sh
138
+ sh scripts/verificar.sh
139
+ ```
140
+
141
+ O desenvolvimento e os testes usam Docker, sem instalar Python, pip ou frameworks
142
+ no host. Nenhum comando publica no PyPI ou faz deploy.
@@ -0,0 +1,118 @@
1
+ # Mailcore para Python
2
+
3
+ [Código-fonte](https://github.com/mailcore-hq/mailcore-python) · [Problemas e sugestões](https://github.com/mailcore-hq/mailcore-python/issues)
4
+
5
+ Cliente Python da API Mailcore, em versão `0.1.0a1`. O pacote de distribuição é
6
+ `mailcore-sdk` e o módulo importado é `mailcore`. Esta versão ainda não foi
7
+ publicada no PyPI. Requer Python 3.11 ou superior.
8
+
9
+ ## Instalação local e primeiro envio
10
+
11
+ Depois da verificação Docker, instale o arquivo local em sua aplicação Python:
12
+
13
+ ```sh
14
+ python -m pip install /caminho/mailcore_sdk-0.1.0a1-py3-none-any.whl
15
+ ```
16
+
17
+ O pip pode buscar as dependências no PyPI; o arquivo do próprio SDK é local.
18
+ Também é possível instalar o `.tar.gz` de `dist/`, ou o checkout em modo editável
19
+ durante o desenvolvimento da sua aplicação.
20
+
21
+ ```python
22
+ import os
23
+ from mailcore import Cliente, PedidoEmail
24
+
25
+ pedido: PedidoEmail = {
26
+ "remetente": "Equipe <equipe@example.com>",
27
+ "destinatarios": ["pessoa@example.com"],
28
+ "assunto": "Bem-vindo",
29
+ "html": "<p>Sua conta está pronta.</p>",
30
+ }
31
+ with Cliente(chave_api=os.environ["MAILCORE_API_KEY"]) as cliente:
32
+ resposta = cliente.emails.enviar(pedido, chave_idempotencia="pedido-123-confirmacao")
33
+ print(resposta["id"])
34
+ ```
35
+
36
+ Use a chave somente no servidor, com remetente de domínio verificado. A aceitação
37
+ da intenção não comprova entrega SMTP. Preserve chave e conteúdo ao repetir uma
38
+ intenção; o SDK não faz tentativas automáticas após erro ou timeout.
39
+
40
+ ## Cliente assíncrono
41
+
42
+ ```python
43
+ from mailcore import ClienteAsync, PedidoEmail
44
+
45
+ async def enviar(pedido: PedidoEmail) -> str:
46
+ async with ClienteAsync() as cliente:
47
+ resposta = await cliente.emails.enviar(pedido, chave_idempotencia="pedido-123-confirmacao")
48
+ return resposta.dados["id"]
49
+ ```
50
+
51
+ Os dois clientes oferecem os mesmos recursos. As chamadas async usam HTTPX
52
+ assíncrono nativo. Reutilize o cliente durante a vida da aplicação; `with`,
53
+ `async with` ou `fechar()` liberam os pools. Não compartilhe um cliente async
54
+ entre event loops. A chave pode ser uma função sem argumentos, resolvida por
55
+ operação, para permitir rotação sem alterar um estado global.
56
+ Um fornecedor que retorne uma chave inválida falha; não usa outra chave do ambiente.
57
+ O pool padrão permite até 100 conexões e 20 ociosas, configuráveis por
58
+ `maximo_conexoes` e `conexoes_ociosas`.
59
+
60
+ ## Respostas e erros
61
+
62
+ `Resposta.dados` preserva os campos e envelopes JSON da API. `status` e
63
+ `cabecalhos` expõem metadados HTTP, e `resposta["id"]` abrevia o acesso ao JSON.
64
+ Bytes de downloads são `bytes`; prévias HTML são `str`. `ErroApi` preserva código
65
+ e status da rejeição; `ErroTransporte` trata rede/prazo; `ErroResposta` trata
66
+ conteúdo inválido ou excedido; `ErroConfiguracao` trata opções inválidas.
67
+
68
+ O cliente exige TLS verificado, não segue redirecionamentos, não armazena cookies
69
+ e não herda proxies do ambiente. Uma CA privada pode ser informada por
70
+ `certificado_ca`. Somente loopback pode usar HTTP com `permitir_http_local=True`.
71
+ `transporte_http` permite um adaptador HTTPX explicitamente fornecido pela aplicação;
72
+ esse adaptador e sua política de rede são responsabilidade de quem o configura.
73
+
74
+ `tempo_limite` (30 segundos por padrão) segue a semântica HTTPX: limite de
75
+ inatividade de cada etapa de conexão, pool, escrita ou leitura. **Não é um prazo
76
+ total de relógio**. A conexão tem limite adicional de 10 segundos. Uma aplicação
77
+ async que precise de prazo total pode envolver a operação em `asyncio.timeout()`;
78
+ o cancelamento é propagado e a resposta é fechada. Um prazo vencido não desfaz
79
+ uma intenção já aceita.
80
+
81
+ A resposta tem teto padrão de 16 MiB, inclusive em fluxo, ajustável por operação
82
+ com `limite_resposta_bytes`. O JSON de pedido admite até 50 MiB para o envelope de
83
+ importação CSV; os limites específicos de cada DTO pertencem à API. A comunicação
84
+ solicita `Accept-Encoding: identity` e recusa codificações não negociadas.
85
+
86
+ ## Downloads e webhooks
87
+
88
+ ```python
89
+ with Cliente() as cliente:
90
+ with cliente.recebimento.baixar_original_fluxo("id-da-mensagem") as fluxo:
91
+ with open("mensagem.eml.parcial", "wb") as arquivo:
92
+ for parte in fluxo:
93
+ arquivo.write(parte)
94
+ ```
95
+
96
+ No cliente async, use `async with` e `async for`. Os fluxos preservam bytes MIME,
97
+ exigem consumo dentro do contexto e liberam a conexão ao terminar, interromper
98
+ ou cancelar. Só trate arquivos parciais como completos depois de sucesso.
99
+ Downloads usam rotas autenticadas; URLs temporárias de metadados não são acessadas
100
+ automaticamente.
101
+
102
+ `verificar_assinatura(corpo_bruto, cabecalhos, segredo)` verifica HMAC-SHA256
103
+ `svix-*`, com comparação em tempo constante e janela de cinco minutos. Passe o
104
+ corpo bruto antes de interpretar JSON. Deduplicação e confirmação durável dos
105
+ eventos pertencem ao consumidor.
106
+
107
+ ## Documentação e desenvolvimento
108
+
109
+ Consulte [cobertura](docs/cobertura.md), [integrações Flask/FastAPI/Django](docs/integracoes.md)
110
+ e [verificação](docs/verificacao.md). Não há dependência de framework no runtime
111
+ do pacote. Instalação local usa os arquivos `.whl`/`.tar.gz` preparados em `dist/`.
112
+
113
+ ```sh
114
+ sh scripts/verificar.sh
115
+ ```
116
+
117
+ O desenvolvimento e os testes usam Docker, sem instalar Python, pip ou frameworks
118
+ no host. Nenhum comando publica no PyPI ou faz deploy.
@@ -0,0 +1,43 @@
1
+ # Segurança
2
+
3
+ Esta distribuição está em alpha (`0.1.0a1`) e ainda não foi publicada no PyPI.
4
+ O pacote de distribuição é `mailcore-sdk`; o módulo importado é `mailcore`.
5
+ As linhas CPython adotadas na matriz são 3.11–3.15, com versões corrigidas das
6
+ dependências do projeto.
7
+
8
+ ## Relatar um problema
9
+
10
+ Antes da publicação, comunique vulnerabilidades por um canal privado acordado com
11
+ os mantenedores. Informe versão, comportamento observado, impacto e uma reprodução
12
+ mínima com dados sintéticos. Não inclua chaves, segredos de webhook, MIME de
13
+ clientes, destinatários reais ou URLs temporárias de download em relatos públicos.
14
+ Não há prazo de resposta ou correção garantido nesta etapa alpha.
15
+
16
+ ## Proteções e responsabilidades
17
+
18
+ O transporte padrão verifica TLS, aceita CA privada, recusa redirecionamentos,
19
+ solicita `identity`, recusa outra codificação e não usa cookies ou proxies do
20
+ ambiente. A opção de HTTP é explícita e limitada a loopback. Transportes injetados
21
+ para testes ou integração têm sua política de rede sob responsabilidade de quem
22
+ os configura.
23
+
24
+ O limite padrão de resposta é 16 MiB, também durante downloads e erros. O teto de
25
+ JSON de pedido é 50 MiB; ele não substitui o limite de CSV de 8 MiB ou limites
26
+ menores dos endpoints. Os timeouts HTTPX são por inatividade de etapa, não um
27
+ prazo total. Nenhum timeout ou cancelamento comprova que um envio foi desfeito.
28
+
29
+ Use chaves somente no servidor, com o menor escopo necessário. Preserve uma chave
30
+ de idempotência estável para repetir explicitamente uma intenção. Separe clientes
31
+ e credenciais entre organizações. Feche pools e respostas em fluxo pelos gestores
32
+ de contexto; evite registrar corpos, dados pessoais ou cabeçalhos de autenticação.
33
+
34
+ Verifique webhooks antes de interpretar seus efeitos, usando o corpo bruto e o
35
+ segredo correto. A janela de timestamp não substitui a deduplicação do
36
+ identificador do evento na aplicação. Original e anexos recebidos devem ser
37
+ tratados como conteúdo externo; a disponibilidade obedece à retenção do servidor.
38
+ Os downloads do SDK usam rotas autenticadas e não navegam automaticamente nas
39
+ URLs temporárias contidas nos metadados.
40
+
41
+ Os exemplos HTTP exigem um token próprio e enviam uma mensagem fixa. A aplicação
42
+ que os hospedar continua responsável por entrada HTTPS, limites do servidor,
43
+ autorização de negócio, proteção de logs e gestão de segredos.
@@ -0,0 +1,148 @@
1
+ # Cobertura do SDK Python
2
+
3
+ A versão `0.1.0a1`, distribuída como `mailcore-sdk` e importada como `mailcore`,
4
+ oferece **16 recursos principais, 30 classes e 151 operações por variante**.
5
+ `Cliente` e `ClienteAsync` totalizam 60 classes de recursos e 302 métodos. As duas
6
+ variantes acessam o mesmo contrato HTTP; esses números não representam 302 rotas
7
+ distintas. Esta versão alpha ainda não foi publicada no PyPI.
8
+
9
+ A superfície preserva o contrato dos SDKs Mailcore Ruby e Node. O
10
+ [ensaio de API local](integracao-api.json) identifica a revisão do backend e o
11
+ SHA256 das fontes usados na validação. Revisão Git e hash de conteúdo são
12
+ identificadores distintos; o relatório registra o estado do ensaio, não uma
13
+ afirmação sobre o HEAD atual do backend.
14
+
15
+ ## Recursos
16
+
17
+ Os caminhos abaixo partem de uma instância de `Cliente` ou `ClienteAsync`.
18
+
19
+ | Recurso principal | Superfície incluída | Operações por variante |
20
+ | ----------------------- | -------------------------------------------------------------------------------------- | ---------------------: |
21
+ | `emails` | Enviar, listar, obter, reagendar, cancelar, eventos, consulta por idempotência e anexo | 8 |
22
+ | `lotes` | Enviar e consultar por idempotência | 2 |
23
+ | `dominios` | CRUD, capacidades, tracking, registros DNS e verificação | 7 |
24
+ | `chaves_api` | Criar, listar, renomear e revogar | 4 |
25
+ | `contatos` | CRUD, lote, segmentos, tópicos, importações e exportações | 20 |
26
+ | `segmentos` | Criar, listar, obter, atualizar e remover | 5 |
27
+ | `topicos` | Criar, listar, obter, atualizar e remover | 5 |
28
+ | `propriedades_contatos` | Criar, listar, obter, atualizar e remover | 5 |
29
+ | `modelos` | CRUD, publicação, duplicação, prévias, testes, versões e pastas | 18 |
30
+ | `webhooks` | CRUD, eventos, tentativas e reenvio | 9 |
31
+ | `campanhas` | CRUD, duplicação, revisão, envio/cancelamento, prévias, testes e métricas | 15 |
32
+ | `automacoes` | CRUD, duplicação, parada e consulta de execuções | 9 |
33
+ | `eventos` | Definição, consulta, edição, remoção, listagem e envio de ocorrência | 6 |
34
+ | `recebimento` | Consulta, anexos, original MIME e downloads comuns ou em fluxo | 8 |
35
+ | `caixas` | CRUD, etiquetas, conversas, mensagens, respostas, encaminhamento e rascunhos | 28 |
36
+ | `cli` | Listar e revogar concessões | 2 |
37
+ | **Total** | **151 operações com a mesma superfície nas duas variantes** | **151** |
38
+
39
+ Os recursos subordinados usam propriedades, como `contatos.exportacoes`,
40
+ `modelos.versoes`, `webhooks.eventos`, `caixas.conversas.emails` e `cli.concessoes`.
41
+ Os nomes dos métodos permanecem iguais no cliente assíncrono: use
42
+ `await cliente.emails.enviar(...)`, sem sufixo adicional no nome.
43
+
44
+ ## Pedidos, respostas e tipagem
45
+
46
+ Pedidos e filtros aceitam `Mapping` com os campos em português do DTO HTTP, como
47
+ `remetente`, `destinatarios`, `agendada_em` e `revisao`. Opções de transporte são
48
+ argumentos nomeados, como `chave_idempotencia`, `tempo_limite` e
49
+ `limite_resposta_bytes`. O servidor continua responsável pelas regras de negócio,
50
+ permissões, revisões e limites específicos de cada endpoint.
51
+
52
+ O transporte aceita valores JSON: chaves String, texto UTF-8, números finitos,
53
+ booleanos, listas, tuplas convertidas em arrays, mapeamentos e `None`. Objetos
54
+ arbitrários, bytes em campos JSON e números não finitos são recusados. Um campo
55
+ omitido difere de um campo enviado com `None`; `None` e `False` permanecem no corpo.
56
+ Na query, `None` é omitido e `False` é enviado como `false`. Identificadores são
57
+ codificados como segmentos, preservando o prefixo da API.
58
+
59
+ As respostas usam `Resposta[T]`, com `dados`, `status` e `cabecalhos` separados.
60
+ JSON mantém chaves String, inclusive nos objetos internos; resposta sem conteúdo
61
+ pode produzir `dados=None`. Downloads comuns retornam `Resposta[bytes]` e prévias
62
+ textuais usam `str`. `resposta["id"]` consulta o campo em `dados`.
63
+
64
+ Há tipos públicos para envio direto/modelo, anexos, lote, respostas de aceitação,
65
+ configuração e opções, além do marcador PEP 561 `py.typed`. Entre eles estão
66
+ `PedidoEmail`, `PedidoEmailLote`, `RespostaEnvio` e `RespostaLote`. Essa tipagem não
67
+ é um schema completo de todas as entidades: recursos complexos ainda aceitam
68
+ `Mapping[str, Any]` e retornam `Resposta[Any]`, preservando o DTO do servidor.
69
+ TypedDict não substitui a validação de runtime nem a validação da API.
70
+
71
+ A paginação é explícita: uma chamada consulta uma página e conserva o envelope e
72
+ os cursores recebidos. O SDK não percorre páginas automaticamente nem uniformiza
73
+ envelopes distintos do backend.
74
+
75
+ ## Contratos relevantes
76
+
77
+ - Envio direto permite composição Cc/Cco, cabeçalhos e anexos Base64/CID conforme
78
+ o DTO. Envio por modelo tem seu próprio formato, sem misturar campos exclusivos
79
+ da composição direta. O cliente não baixa anexos remotos por iniciativa própria.
80
+ - Lotes seguem o array e a aceitação atômica do backend, sem dividir pedidos nem
81
+ repetir automaticamente. Agendamento usa `agendada_em` ISO 8601 com fuso e as
82
+ regras de estado do servidor. Aceitação não comprova entrega SMTP.
83
+ - Importações recebem CSV UTF-8 no corpo JSON. O limite de CSV é 8 MiB no contrato
84
+ da API; o transporte admite JSON de até 50 MiB para acomodar escapes. Esse teto
85
+ geral não amplia limites menores dos demais endpoints.
86
+ - Modelos, campanhas, automações e caixas preservam revisões, estados e campos
87
+ anuláveis. Parar ou cancelar não desfaz efeitos já aceitos pelo servidor.
88
+ - A chave secreta de API só aparece na criação. Revogação pode retornar resposta
89
+ vazia. Concessões CLI incluem consulta/revogação, sem iniciar autenticação
90
+ interativa ou renovar tokens.
91
+
92
+ ## Recebimento e fluxos
93
+
94
+ Original MIME, anexos recebidos e exportações usam rotas fixas autenticadas. URLs
95
+ temporárias presentes nos metadados são capacidades sensíveis; o SDK não as segue
96
+ automaticamente nem transfere a credencial para um destino informado pela API.
97
+ Retenção e disponibilidade do conteúdo são determinadas pelo servidor.
98
+
99
+ `baixar_original_fluxo`, `baixar_anexo_fluxo` e `baixar_fluxo` devolvem gestores de
100
+ contexto. Use `with` no cliente síncrono e `async with` no assíncrono; dentro do
101
+ contexto, consuma bytes por `for` ou `async for`. O leitor expõe `status`,
102
+ `cabecalhos` e `total_bytes`. O gestor assíncrono não recebe `await` antes de
103
+ `async with`. A saída do contexto fecha a resposta, inclusive no encerramento
104
+ antecipado do consumo.
105
+
106
+ O teto padrão de resposta é **16 MiB**, inclusive em fluxo e em respostas de erro;
107
+ `limite_resposta_bytes` permite ajustar a operação. O limite é aplicado durante a
108
+ leitura, não apenas depois de acumular o conteúdo.
109
+
110
+ ## Transporte e limites
111
+
112
+ Cada cliente reutiliza seu pool HTTPX e deve ser fechado com contexto ou
113
+ `fechar()`; `ClienteAsync` usa fechamento aguardado e também oferece
114
+ `fechar_async()`. A comunicação padrão usa HTTPS verificado, recusa
115
+ redirecionamentos, não armazena cookies e não herda proxies do ambiente. Uma CA
116
+ privada pode ser configurada. HTTP é uma opção explícita restrita a loopback para
117
+ ensaios locais. Um transporte HTTP injetado tem sua política de rede sob controle
118
+ de quem o fornece.
119
+
120
+ O SDK solicita `Accept-Encoding: identity` e recusa codificações não negociadas.
121
+ Isso preserva os bytes de MIME, anexos e CSV e mantém o limite de leitura
122
+ previsível. Nenhuma falha produz repetição automática; preserve chave e conteúdo
123
+ ao decidir repetir uma intenção.
124
+
125
+ `tempo_limite`, por padrão **30 segundos**, limita a inatividade de cada etapa
126
+ HTTPX: pool, conexão, escrita ou leitura. A conexão usa no máximo o menor valor
127
+ entre esse limite e `tempo_conexao`, cujo padrão é **10 segundos**. **Não existe
128
+ um prazo total de relógio embutido**: um fluxo que continua avançando pode durar
129
+ mais. A aplicação assíncrona pode envolver a operação em `asyncio.timeout()` quando
130
+ precisar desse prazo total. Cancelar a espera não desfaz uma intenção aceita.
131
+
132
+ ## Alcance da validação
133
+
134
+ A matriz do projeto é CPython **3.11–3.15**. Os exemplos opcionais exercitam Flask
135
+ e Django síncronos e FastAPI assíncrono; o exemplo Django é uma view direta, sem
136
+ implementar um backend de email. Veja [as integrações](integracoes.md) para as
137
+ versões e seus limites. SDK e frameworks têm contratos de compatibilidade distintos.
138
+
139
+ Os testes de contrato conferem as 151 operações de cada variante com um
140
+ transporte coletor. Ensaios HTTPS sintéticos verificam o transporte; o
141
+ [relatório de API local](integracao-api.json) registra a execução contra backend
142
+ isolado com dados sintéticos. Esses resultados não demonstram entrega SMTP,
143
+ produção, capacidade de escala ou deploy cloud.
144
+
145
+ Esta cobertura descreve a seleção implementada, não toda a API do produto.
146
+ Não inclui recursos globais de logs, uso ou supressões, autorização OAuth
147
+ interativa, helpers de sessão/dashboard nem renderização React. Campos adicionais
148
+ dos DTOs não implicam métodos ou fluxos novos no SDK.
@@ -0,0 +1,27 @@
1
+ {
2
+ "data_utc": "2026-11-14T14:29:54.661686+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:41ff4552dda1fb72f2b97f34efd9a3742a0c31d9a8b19309846a29b0fd74cce4",
17
+ "api_runtime": "sha256:158e95e9b87f2a126c038e6931b2f956fe6120f3566fd047a29250bd7783ff33",
18
+ "compilador": "sha256:8fa55b2f3ddf97471ab6a767bfa3f37e6bad0986ba823e75fea57e2a2a5c3073",
19
+ "postgres": "sha256:f1ea2a1a4fb7b68d4836be1fdcb8a6466a89b072158bf9d97663600538c294c5"
20
+ },
21
+ "backend_revisao": "c00da750773435140fda072b88c7070fdfda22c7",
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,52 @@
1
+ {
2
+ "data_utc": "2026-11-14T14:58:46.486693+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_repr",
15
+ "envio_composto_idempotencia_409_download_cancelamento",
16
+ "envio_async_replay_mesma_intencao",
17
+ "lote_atomico_repeticao_conflito",
18
+ "dominios_capacidades_tracking_crud",
19
+ "contatos_nulos_catalogos_exportacao_csv_fluxo",
20
+ "modelos_pastas_versoes_previa_publicacao_cas",
21
+ "marketing_crud_sem_disparo",
22
+ "caixas_etiquetas_rascunhos_cas_sem_envio",
23
+ "recebimento_mime_real_pendente_bytes_fluxo",
24
+ "recebimento_async_bytes_contexto_fluxo",
25
+ "cli_revogacao_e_limpeza_crud"
26
+ ],
27
+ "etapa": "concluido",
28
+ "imagens": {
29
+ "cliente": "sha256:a8a48dc98d737ee421afe7422e846c5bf17bdc14033170a4380eae2cd9c5f0d9",
30
+ "api_runtime": "sha256:158e95e9b87f2a126c038e6931b2f956fe6120f3566fd047a29250bd7783ff33",
31
+ "compilador": "sha256:8fa55b2f3ddf97471ab6a767bfa3f37e6bad0986ba823e75fea57e2a2a5c3073",
32
+ "postgres": "sha256:f1ea2a1a4fb7b68d4836be1fdcb8a6466a89b072158bf9d97663600538c294c5"
33
+ },
34
+ "backend_revisao": "27789fd7c7bfbeaaed4a4f517e417921ae8ca60b",
35
+ "backend_fontes_sha256": "d31c825268a64c858276827f47d27e506f543421327f487d7165e511359e248f",
36
+ "backend_fontes_alterados": false,
37
+ "api_compilada_neste_ensaio": true,
38
+ "api_binario_sha256": "3cd977ab90dbfcc0067b7d0b85d120ae9132f4e873cc223298f901fc1dbd35bf",
39
+ "migracoes_aplicadas": 23,
40
+ "python": "3.12.16",
41
+ "httpx": "0.28.1",
42
+ "recebimento_processado_por_worker": false,
43
+ "async_exercitado": true,
44
+ "async_envio_repetiu_intencao_existente": true,
45
+ "persistencia_sintetica": {
46
+ "mensagens": 3,
47
+ "tarefas_envio": 3,
48
+ "recebidos": 1,
49
+ "recebidos_pendentes": 1
50
+ },
51
+ "limpeza_concluida": true
52
+ }