diff --git a/Attrap_bot.py b/Attrap_bot.py
index f8b0c892faa65bec849c69e4357f179a9fac3434..9a0cb037650480516caab17d91276dcc77e3279b 100644
--- a/Attrap_bot.py
+++ b/Attrap_bot.py
@@ -88,18 +88,18 @@ class Attrap_bot:
 
             self.mastodon.toot(message)
 
-    def __init__(self, config_file):
-        # On importe les fichiers de configuration et on surcharge la configuration par défaut
-        config_default_file = yaml.load(open('config.default.yml', 'r'), Loader=yaml.FullLoader)
-        config_file = yaml.load(open(config_file, 'r'), Loader=yaml.FullLoader)
-        self.config = config_default_file | config_file
+    def __init__(self, config):
+        # On surcharge la configuration par défaut avec la configuration donnée
+        config_default = yaml.load(open('config.default.yml', 'r'), Loader=yaml.FullLoader)
+        self.config = config_default | config
 
         logger.info('Démarrage de Attrap_bot')
         logger.info(f"Source des données : {self.config['data_source']}")
 
     def analyze(self):
-        for query in self.config['queries']:
-            query_id = query['id']
+        for i in self.config['queries']:
+            query = self.config['queries'][i]
+            query_id = i
             search = query['search']
             administration = query['administration']
             if not administration:
@@ -199,27 +199,25 @@ class Attrap_bot:
 
     def get_mastodon_sender(self, config_id):
         if self.config.get('mastodon'):
-            for i in self.config['mastodon']:
-                if i.get('id') and i['id'] == config_id and i['instance'] and i['access_token']:
-                    return Attrap_bot.Mastodon_sender(i['instance'], i['access_token'])
-        logger.warning(f"La configuration Mastodon est invalide (id: {query['mastodon']})")
+            if self.config['mastodon'].get(config_id) and self.config['mastodon'][config_id]['instance'] and self.config['mastodon'][config_id]['access_token']:
+                return Attrap_bot.Mastodon_sender(self.config['mastodon'][config_id]['instance'], self.config['mastodon'][config_id]['access_token'])
+        logger.warning(f"La configuration Mastodon est invalide (id: {config_id})")
         return None
 
     def get_smtp_sender(self, config_id):
         if self.config.get('smtp'):
-            for i in self.config['smtp']:
-                if i.get('id') and i['id'] == config_id and i['hostname']:
-                    smtp_sender = Attrap_bot.SMTP_sender(i['hostname'])
-                    if i.get('username'):
-                        smtp_sender.set_username(i['username'])
-                    if i.get('password'):
-                        smtp_sender.set_password(i['password'])
-                    if i.get('port'):
-                        smtp_sender.set_port(i['port'])
-                    if i.get('ssl'):
-                        smtp_sender.set_ssl(i['ssl'])
-                    if i.get('starttls'):
-                        smtp_sender.set_starttls(i['starttls'])
+                if self.config['smtp'].get(config_id) and self.config['smtp'][config_id]['hostname']:
+                    smtp_sender = Attrap_bot.SMTP_sender(self.config['smtp'][config_id]['hostname'])
+                    if self.config['smtp'][config_id].get('username'):
+                        smtp_sender.set_username(self.config['smtp'][config_id]['username'])
+                    if self.config['smtp'][config_id].get('password'):
+                        smtp_sender.set_password(self.config['smtp'][config_id]['password'])
+                    if self.config['smtp'][config_id].get('port'):
+                        smtp_sender.set_port(self.config['smtp'][config_id]['port'])
+                    if self.config['smtp'][config_id].get('ssl'):
+                        smtp_sender.set_ssl(self.config['smtp'][config_id]['ssl'])
+                    if self.config['smtp'][config_id].get('starttls'):
+                        smtp_sender.set_starttls(self.config['smtp'][config_id]['starttls'])
                     return smtp_sender
-        logger.warning(f"La configuration Mastodon est invalide (id: {query['mastodon']})")
+        logger.warning(f"La configuration Mastodon est invalide (id: {config_id})")
         return None
diff --git a/README.md b/README.md
index 88aef6af548d97d8e00c2b3141f46422ac4df55b..90125dc10eec783e9bf33f14a3c52c8b58ee934c 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,10 @@ source bin/activate
 pip3 install -r requirements.txt
 ```
 
+## Utilisation
+
+Les analyses se lancent avec `cli.py`. L'option `--config` ou `-c` permet de spécifier le fichier de configuration (par défaut, `config.yml`).
+
 ## Configuration
 
 Vous pouvez configurer le robot avec un fichier de configuration ou avec des variables d'environnement.
@@ -24,14 +28,14 @@ Les requêtes sont spécifiées dans la section `queries`, les paramètres pour
 
 ```yaml
 queries:
-  - id: vsa-paris
+  vsa_paris:
     search: "\"traitement algorithmique\" AND 2023-380"
     administration: "ppparis,pref75"
     email:
       from: "attrap@example.org"
       to: "vsa@example.org"
       smtp: example_smtp
-  - id: vsa
+  vsa:
     search: "\"traitement algorithmique\" AND 2023-380"
     administration:
     mastodon:
@@ -40,23 +44,48 @@ queries:
       instance: mastodon_social
 
 smtp:
-  - id: example_smtp
+  example_smtp:
     hostname: "smtp.example.org"
     port: 587
     username: "attrap@example.org"
-    password: "secr3t"
+    password: "secr3t!"
     ssl: False
     starttls: True
 
 mastodon:
-  - id: mastodon_social
+  mastodon_social:
     instance: "mastodon.social"
     access_token: "a-secret-access-token-here"
 ```
 
 ### Variables d'environnement
 
-TODO
+Toutes les variables commencent par `ATTRAP_BOT__`. Les noms des variables sont en majuscules et correspondent à leur équivalent dans `config.yml`, séparés de `__`. Pour lancer une analyse avec la même configuration que ci-dessus, utilisez les variables suivantes :
+
+```bash
+ATTRAP_BOT__QUERIES__VSA_PARIS__SEARCH="\"traitement algorithmique\" AND 2023-380" \
+ATTRAP_BOT__QUERIES__VSA_PARIS__SEARCH="ppparis,pref75" \
+ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__FROM="attrap@example.org" \
+ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__TO="vsa@example.org" \
+ATTRAP_BOT__QUERIES__VSA_PARIS__EMAIL__SMTP="example_smtp" \
+ATTRAP_BOT__QUERIES__VSA__SEARCH="\"traitement algorithmique\" AND 2023-380" \
+ATTRAP_BOT__QUERIES__VSA__MASTODON__PREFIX="[{administration_name}]" \
+ATTRAP_BOT__QUERIES__VSA__MASTODON__SUFFIX="#{id} #{administration}" \
+ATTRAP_BOT__QUERIES__VSA__MASTODON__INSTANCE="mastodon_social" \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__HOSTNAME="smtp.example.org" \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__PORT=587 \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__USERNAME="attrap@example.org" \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__PASSWORD="secr3t\!" \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__SSL="False" \
+ATTRAP_BOT__SMTP__EXAMPLE_SMTP__STARTTLS="True" \
+ATTRAP_BOT__MASTODON__MASTODON_SOCIAL__INSTANCE="mastodon.social" \
+ATTRAP_BOT__MASTODON__MASTODON_SOCIAL__ACCESS_TOKEN="a-secret-access-token-here" \
+./cli.py
+```
+
+Pour spécifier une valeur boléenne, vous pouvez indiquer `0`/`1`, `False`/`True` ou `FalseFalse`/`TrueTrue` (sensible à la casse).
+
+La configuration par variables d'environnement est ignorée si un fichier `config.yml` est trouvé.
 
 ## Développement
 
diff --git a/cli.py b/cli.py
index cbcd07bd61634e6a0bd5509f2372c4458531064b..a9b64e7d1faa4d6ee3161360cd3382d311f99da0 100755
--- a/cli.py
+++ b/cli.py
@@ -1,6 +1,19 @@
 #!/usr/bin/env python3
 
 import argparse
+import os
+import re
+import sys
+import yaml
+
+def create_config(keys, value):
+    if type(value) == str:
+        value = value.replace('FalseFalse', False).replace(0, False).replace('False', False)
+        value = value.replace('TrueTrue', True).replace(1, True).replace('True', True)
+    if not keys[:-1] == []:
+        return create_config(keys[:-1], {keys[-1]: value})
+    else:
+        return {keys[-1]: value}
 
 from Attrap_bot import Attrap_bot
 
@@ -22,5 +35,17 @@ if args.config:
 else:
     config_file = 'config.yml'
 
-bot = Attrap_bot(config_file)
+# Si le fichier de configuration existe, on le charge
+if os.path.isfile(config_file):
+    config = yaml.load(open(config_file, 'r'), Loader=yaml.FullLoader)
+# Sinon, on crée la configuration à partir des variables d'environnement
+else:
+    config = {}
+    for var in os.environ:
+        if var.startswith('ATTRAP_BOT__'):
+            key = var[12:].lower()
+            value = os.environ[var]
+            config = config | create_config(re.split('__', key), value)
+
+bot = Attrap_bot(config)
 bot.analyze()