# Envoyer des e-mails avec Python

> Envoyez des e-mails depuis Python avec le SDK emailit, le backend SMTP de Django ou smtplib, et vérifiez les webhooks Emailit dans Flask ou Django.

Ce guide montre comment envoyer des e-mails depuis Python avec le paquet officiel `emailit`, comment utiliser plutôt le SMTP depuis Django ou la bibliothèque standard, et comment vérifier les signatures des webhooks dans Flask et Django.

## Prérequis

- Python 3.7 ou version ultérieure.
- Un [domaine d’envoi vérifié](/fr/docs/domains/add-a-domain/), par exemple `acme.com`.
- Une [clé API](/fr/docs/developers/api-keys/). Une clé Sending Only limitée à votre domaine suffit.
- Tant que votre espace de travail n’a pas l’[accès production](/fr/docs/workspaces/production-access/), vous ne pouvez envoyer qu’aux adresses e-mail des comptes des membres de l’espace de travail.

## Installer le SDK

```bash
pip install emailit
```

Le SDK s’appuie sur `requests`.

## Configurer votre clé API

Définissez la clé dans l’environnement de votre serveur d’application, de votre worker ou de votre conteneur :

```bash
```

Lisez-la avec `os.environ["EMAILIT_API_KEY"]` : une clé absente provoque ainsi une erreur visible dès le démarrage plutôt qu’au premier envoi.

## Envoyer un 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_…
```

Créez le client une seule fois et réutilisez-le. Le même appel `send` accepte `cc`, `bcc`, `reply_to`, `attachments`, `template` avec `variables`, `scheduled_at` et `tracking` ; consultez [Envoyer un e-mail](/fr/docs/api-reference/emails/send/).

### Gérer les erreurs

```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)
```

Le SDK est synchrone. Dans les frameworks asynchrones comme FastAPI, appelez-le depuis un endpoint `def` classique ou une tâche en arrière-plan pour ne pas bloquer la boucle d’événements.

## Envoyer plutôt via SMTP

### Django

Le backend SMTP intégré de Django fonctionne avec Emailit. Ajoutez ceci à `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"
```

Utilisez ensuite les fonctions d’envoi de Django comme d’habitude :

```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` et `SERVER_EMAIL` doivent utiliser un domaine d’envoi vérifié. Pour le TLS implicite sur le port 465, définissez `EMAIL_PORT = 465` et `EMAIL_USE_SSL = True` au lieu de `EMAIL_USE_TLS`.

### Bibliothèque standard

Sans framework, utilisez `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)
```

Si votre réseau bloque le port 587, utilisez le port 2525 ou 2587 avec le même code. Consultez [Paramètres SMTP](/fr/docs/smtp/settings/).

## Recevoir des webhooks

[Créez un webhook](/fr/docs/webhooks/set-up/) et enregistrez son secret de signature dans `EMAILIT_WEBHOOK_SECRET`. Vérifiez la signature à partir du corps brut de la requête avec cette fonction utilitaire :

```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)
```

Appelez-la ensuite depuis la route de votre 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)
```

Renvoyez un `2xx` dans les 30 secondes ; pour toute autre réponse, Emailit réessaie. Consultez [Signature des requêtes](/fr/docs/webhooks/request-signature/).

## Conseils pour la production

- **Envoyez depuis une file de tâches.** Utilisez Celery, RQ ou les tâches en arrière-plan de votre framework pour les envois en masse, et limitez le débit des workers pour rester sous vos [limites d’envoi](/fr/docs/limits/) (2 e-mails par seconde par défaut).
- **Sécurisez les relances.** Quand une tâche est relancée après un timeout, envoyez un en-tête `Idempotency-Key` (avec `requests` ou `httpx`) pour que l’e-mail ne soit pas envoyé deux fois. Consultez [Idempotence](/fr/docs/email-api/idempotency/).
- **Dédoublonnez les webhooks.** Enregistrez chaque `event_id` traité : Emailit réessaie les livraisons en échec, qui peuvent donc arriver plusieurs fois.
- **Utilisez une clé par environnement** et chargez-la depuis l’environnement ou un gestionnaire de secrets. Ne l’écrivez jamais en dur dans `settings.py`.

## Étapes suivantes

  - [Envoyer des e-mails avec l’API](/fr/docs/email-api/send-email/): Pièces jointes, programmation et suivi.
  - [Modèles](/fr/docs/templates/): Concevez vos e-mails dans Emailit et envoyez-les par alias.
  - [Types d’événements webhook](/fr/docs/webhooks/event-types/): Tous les événements et leur payload.
  - [SDK et bibliothèques](/fr/docs/sdks/): Toutes les bibliothèques officielles.

---
Source: https://emailit.com/fr/docs/frameworks/python/
