# Enviar e-mails com Python

> Envie e-mails do Python com o SDK emailit, com o backend SMTP do Django ou com o smtplib, e verifique os webhooks do Emailit no Flask ou no Django.

Este guia mostra como enviar e-mails do Python com o pacote oficial `emailit`, como usar SMTP a partir do Django ou da biblioteca padrão como alternativa e como verificar as assinaturas dos webhooks no Flask e no Django.

## Pré-requisitos

- Python 3.7 ou mais recente.
- Um [domínio de envio verificado](/pt/docs/domains/add-a-domain/), por exemplo, `acme.com`.
- Uma [chave de API](/pt/docs/developers/api-keys/). Uma chave Sending Only restrita ao seu domínio é suficiente.
- Até o seu workspace ter [acesso de produção](/pt/docs/workspaces/production-access/), você só pode enviar para os e-mails das contas dos membros do workspace.

## Instalar o SDK

```bash
pip install emailit
```

Por baixo dos panos, o SDK usa o `requests`.

## Configurar a chave de API

Defina a chave no ambiente do seu servidor de aplicação, worker ou contêiner:

```bash
```

Leia-a com `os.environ["EMAILIT_API_KEY"]`, para que uma chave ausente gere um erro claro na inicialização, e não no primeiro envio.

## Enviar um e-mail

```python title="send.py"

from emailit import EmailitClient

client = EmailitClient(os.environ["EMAILIT_API_KEY"])

email = client.emails.send({
    "from": "Acme <hello@acme.com>",
    "to": "ada@example.com",
    "subject": "Reset your password",
    "html": "<p>Use this link to reset your password.</p>",
    "text": "Use this link to reset your password.",
})

print(email.id)  # em_…
```

Crie o cliente uma vez e reutilize-o. A mesma chamada `send` aceita `cc`, `bcc`, `reply_to`, `attachments`, `template` com `variables`, `scheduled_at` e `tracking`; consulte [Enviar um e-mail](/pt/docs/api-reference/emails/send/).

### Tratar erros

```python
from emailit import (
    ApiErrorException,
    AuthenticationException,
    RateLimitException,
    UnprocessableEntityException,
)

try:
    client.emails.send(message)
except RateLimitException:
    # 429: wait and retry, for example from a task queue
    raise
except AuthenticationException:
    # 401: the API key is missing or invalid
    raise
except UnprocessableEntityException as e:
    # 422: for example, the from domain isn't verified
    print(e.json_body)
except ApiErrorException as e:
    print(e.http_status, e.json_body)
```

O SDK é síncrono. Em frameworks assíncronos como o FastAPI, chame-o a partir de um endpoint `def` comum ou de uma background task para que ele não bloqueie o event loop.

## Enviar por SMTP como alternativa

### Django

O backend SMTP nativo do Django funciona com o Emailit. Adicione isto ao `settings.py`:

```python title="settings.py"

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.emailit.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "emailit"
EMAIL_HOST_PASSWORD = os.environ["EMAILIT_API_KEY"]
DEFAULT_FROM_EMAIL = "Acme <hello@acme.com>"
SERVER_EMAIL = "alerts@acme.com"
```

Depois, use as funções de e-mail do Django como de costume:

```python
from django.core.mail import send_mail

send_mail(
    subject="Reset your password",
    message="Use this link to reset your password.",
    from_email=None,  # uses DEFAULT_FROM_EMAIL
    recipient_list=["ada@example.com"],
    html_message="<p>Use this link to reset your password.</p>",
)
```

`DEFAULT_FROM_EMAIL` e `SERVER_EMAIL` devem usar um domínio de envio verificado. Para TLS implícito na porta 465, defina `EMAIL_PORT = 465` e `EMAIL_USE_SSL = True` em vez de `EMAIL_USE_TLS`.

### Biblioteca padrão

Sem framework, use o `smtplib`:

```python title="send_smtp.py"
from email.message import EmailMessage

message = EmailMessage()
message["From"] = "Acme <hello@acme.com>"
message["To"] = "ada@example.com"
message["Subject"] = "Reset your password"
message.set_content("Use this link to reset your password.")
message.add_alternative("<p>Use this link to reset your password.</p>", subtype="html")

with smtplib.SMTP("smtp.emailit.com", 587) as smtp:
    smtp.starttls()
    smtp.login("emailit", os.environ["EMAILIT_API_KEY"])
    smtp.send_message(message)
```

Se a sua rede bloquear a 587, use a porta 2525 ou 2587 com o mesmo código. Consulte [Configurações de SMTP](/pt/docs/smtp/settings/).

## Receber webhooks

[Crie um webhook](/pt/docs/webhooks/set-up/) e guarde o segredo de assinatura dele em `EMAILIT_WEBHOOK_SECRET`. Verifique a assinatura com base no corpo bruto da requisição com este helper:

```python title="emailit_webhooks.py"

def is_valid_signature(raw_body: bytes, signature: str, timestamp: str) -> bool:
    if not signature or not timestamp or not timestamp.isdigit():
        return False
    if abs(time.time() - int(timestamp)) > 300:
        return False
    secret = os.environ["EMAILIT_WEBHOOK_SECRET"].encode()
    expected = hmac.new(secret, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

Depois, chame-o a partir da rota do seu framework:

**Flask**

```python title="app.py"
import json

from flask import Flask, abort, request

from emailit_webhooks import is_valid_signature

app = Flask(__name__)

@app.post("/webhooks/emailit")
def emailit_webhook():
    raw_body = request.get_data()
    if not is_valid_signature(
        raw_body,
        request.headers.get("X-Emailit-Signature", ""),
        request.headers.get("X-Emailit-Timestamp", ""),
    ):
        abort(401)

    for event in json.loads(raw_body):  # an array of up to 100 events
        if event["type"] == "email.bounced":
            address = event["data"]["object"]["to"]
            # Stop emailing this address.

    return "", 200
```

**Django**

```python title="webhooks/views.py"
import json

from django.http import HttpResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from emailit_webhooks import is_valid_signature

@csrf_exempt
@require_POST
def emailit_webhook(request):
    if not is_valid_signature(
        request.body,
        request.headers.get("X-Emailit-Signature", ""),
        request.headers.get("X-Emailit-Timestamp", ""),
    ):
        return HttpResponseForbidden("Invalid signature")

    for event in json.loads(request.body):  # an array of up to 100 events
        if event["type"] == "email.complained":
            address = event["data"]["object"]["to"]
            # Stop emailing this address.

    return HttpResponse(status=200)
```

Retorne um `2xx` em até 30 segundos; as outras respostas recebem novas tentativas. Consulte [Assinatura das requisições](/pt/docs/webhooks/request-signature/).

## Dicas para produção

- **Envie a partir de uma fila de tarefas.** Use Celery, RQ ou as background tasks do seu framework para e-mails em massa e limite o ritmo dos workers para ficar dentro dos seus [limites de envio](/pt/docs/limits/) (2 e-mails por segundo por padrão).
- **Torne as novas tentativas seguras.** Quando uma tarefa tentar de novo depois de um timeout, envie um cabeçalho `Idempotency-Key` (com `requests` ou `httpx`) para que o e-mail não seja enviado duas vezes. Consulte [Idempotência](/pt/docs/email-api/idempotency/).
- **Elimine webhooks duplicados.** Guarde cada `event_id` que você processa; as entregas com falha recebem novas tentativas e podem chegar mais de uma vez.
- **Use uma chave por ambiente** e carregue-a do ambiente ou de um gerenciador de segredos. Nunca a fixe no código do `settings.py`.

## Próximos passos

  - [Enviar e-mails com a API](/pt/docs/email-api/send-email/): Anexos, agendamento e rastreamento.
  - [Templates](/pt/docs/templates/): Crie e-mails no Emailit e envie-os pelo alias.
  - [Tipos de evento de webhook](/pt/docs/webhooks/event-types/): Todos os eventos e os payloads deles.
  - [SDKs e bibliotecas](/pt/docs/sdks/): Todas as bibliotecas oficiais.

---
Fonte: https://emailit.com/pt/docs/frameworks/python/
