# Référence de l’API HTTP

## Langues et codes d'erreur

L'anglais est la langue par défaut. L'API, la console Web et les moteurs de rendu de notifications partagent les catalogues intégrés dans `internal/i18n/locales/` . `internal/i18n/languages.json` définit ces codes canoniques et noms natifs :

| Code      | Langue             |
| --------- | ------------------ |
| `en`      | English            |
| `zh-CN`   | 简体中文           |
| `zh-Hant` | 繁體中文           |
| `ja`      | 日本語             |
| `ko`      | 한국어             |
| `de`      | Deutsch            |
| `fr`      | Français           |
| `es`      | Español            |
| `pt-BR`   | Português (Brasil) |

- Lors de la première visite, la console utilise le `Accept-Language` du navigateur. Son sélecteur stocke localement le code canonique choisi. Le changement de langue de la console laisse les paramètres de notification enregistrés inchangés.
- Les requêtes API, y compris les requêtes de gestion d'applications, négocient `Accept-Language` par pondérations de qualité. Les réponses incluent `Content-Language` et `Vary: Accept-Language` . Les préférences non prises en charge sont ignorées ; Les préférences manquantes, invalides ou totalement non prises en charge reviennent à `en` . Une langue de poids 0 est exclue de la négociation.
- Les variantes régionales correspondent à leur langue prise en charge : `fr-CA` → `fr` , `es-MX` → `es` , `pt` / `pt-PT` → `pt-BR` . L'écriture chinoise est respectée : `zh-Hans` / `zh-CN` / `zh-SG` → `zh-CN` ; `zh-Hant` / `zh-TW` / `zh-HK` / `zh-MO` → `zh-Hant` . Un script explicite a priorité sur la région ; le simple `zh` utilise le chinois simplifié.
- `GET /locales/default.json` sert le catalogue négocié ; `GET /locales/{canonical-code}.json` sert explicitement ce catalogue. Les chemins de catalogue inconnus renvoient `locale_not_supported` .
- Paramètres de livraison `language` contrôle les notifications générées par l'avant-poste indépendamment de la langue de la demande ou de la console. Il accepte les codes canoniques et les variantes régionales prises en charge, conserve le code canonique et prend par défaut la valeur `en` lorsqu'il est omis ou vide. Une valeur explicite non prise en charge renvoie 400 `config_language_invalid` .
- Le service de transmission des notifications génère un texte de notification de test natif en utilisant la langue de l'appareil couplé. La langue de notification de l'avant-poste ne remplace pas cette langue. Le service de transmission des notifications et l'application prennent chacun en charge leur propre ensemble de langues.
- Les rôles de dossier standard et les noms de dossiers courants sont localisés dans la console. Les chemins IMAP bruts, les noms personnalisés et `folder_roles` restent inchangés dans les API ; Les applications localisent ces rôles avec leurs propres catalogues. Les titres des notifications continuent d'utiliser le nom du dossier feuille d'origine. Les noms saisis par l'utilisateur, les objets des e-mails, le contenu des e-mails et les codes de vérification restent littéraux.
- Les journaux et la sortie CLI utilisent l’anglais. Les erreurs commerciales conservent les codes `snake_case` anglais stables et les paramètres nommés ; le stockage conserve les codes et les paramètres, et les couches d'affichage traduisent les messages. Les dates et les chiffres dans les réponses API conservent leurs formats lisibles par machine existants ; la console formate les horodatages en utilisant les paramètres régionaux choisis.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Sign in with a valid session or API token."
  }
}
```

`last_error` dans les enregistrements d’envoi et `notice` dans l’état des dossiers utilisent la même structure `{code, message, params?}`, par exemple `bark_http_error` avec `{"status":"503"}`. Utilisez `code` pour la logique et affichez `message`.

Chaque boîte aux lettres réserve une connexion pour sa découverte de dossier et ses tests de connexion. La demande de l'observateur est `realtime Folder count + (1 if any scheduled Folders exist, otherwise 0)` , au maximum des connexions `connection_limit - 1` (9 par défaut) ; toutes les opérations pour cette boîte aux lettres restent ensemble dans `connection_limit` (2–100). Configurations enregistrées avec une limite de 1 démarrage en mode de récupération avec `configuration_invalid` .

## API HTTP

`GET /` sert la page actuelle ; les actifs statiques et `/healthz` sont publics. Avant la configuration, tous les autres points de terminaison de gestion renvoient 403 `setup_required` . `POST /api/v1/setup` est disponible une fois ; `POST /api/v1/session` est public après la configuration. Toutes les API restantes nécessitent le cookie de session ou `Authorization: Bearer mwk_…` .

Les sessions de cookies utilisent HttpOnly, SameSite=Strict, Path=/, Secure sur une connexion TLS ou HTTPS Origin, un délai d'inactivité de sept jours et un délai d'attente absolu de 30 jours. Chaque mutation authentifiée par cookie nécessite `X-CSRF-Token` et un HTTP(S) `Origin` dont l'hôte et le port facultatif correspondent exactement à l'hôte de la demande. Les demandes du porteur ignorent les contrôles CSRF. Un en-tête d'autorisation non valide échoue même lorsqu'un cookie valide est présent.

Les échecs de connexion et de code de configuration partagent un budget d'adresse source : dix échecs toutes les 15 minutes, suivis de 429 `too_many_attempts` avec `Retry-After` en secondes. La source est Gin ClientIP utilisant les plages de proxy de confiance ci-dessus. Les changements de mot de passe utilisent également ce budget. Les origines intersites explicites sont rejetées lors de la connexion et de la configuration. Étant donné que chaque homologue du réseau privé est considéré comme un proxy, un client de votre réseau local peut définir X-Forwarded-For et répartir ses tentatives sur de nombreuses adresses sources. N'exposez pas l'écouteur d'avant-poste à un réseau local non fiable ; publiez-le uniquement via votre proxy inverse.

Tous les corps JSON sont limités à 64 Ko ; les champs inconnus ou le JSON de fin renvoient 400. Les mots de passe comportent au minimum 12 caractères et 1 024 octets UTF-8 au maximum. Les noms d'utilisateur et les noms de jetons comportent entre 1 et 128 octets. Tous les horodatages de l'API de gestion sont des chaînes RFC 3339 au format UTC, y compris la livraison `created_at` / `accepted_at` , le jeton `created_at` / `last_used_at` , les vérifications de dossiers, les heures de journalisation et l'heure de génération des diagnostics. Les secondes fractionnaires préservent la précision stockée. Le jeton inutilisé `last_used_at` est nul ; les livraisons sans acceptation omettent `accepted_at`. Les décomptes, les durées et les révisions restent des chiffres.

401 identifie une session ou un jeton API non valide, ou des informations d'identification rejetées sur `POST /api/v1/session` . Un code de configuration incorrect renvoie 400 `setup_code_invalid` ; l'assistant revient à l'étape de code et conserve le nom d'utilisateur. Un mot de passe actuel incorrect sur `PUT /api/v1/admin/password` renvoie 400 `current_password_invalid` et compte dans la limite d'échec ; la session en cours reste valable. Les échecs de connexion et les erreurs de mot de passe actuel apparaissent dans le formulaire, préservant ainsi ses entrées.

| Méthode            | Chemin                                | Demande / réponse                                                                                                                         |
| ------------------ | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| POST               | `/api/v1/setup`                       | `{ "code":"XXXX-XXXX-XXXX", "username":"admin", "password":"…" }` ; 201, cookie + `{ "csrf_token":"…" }` ; répéter la configuration : 409 |
| POST               | `/api/v1/session`                     | `{ "username":"admin", "password":"…" }` ; 200, cookie + jeton CSRF                                                                       |
| GET                | `/api/v1/session`                     | `{ "authenticated":true, "csrf_token":"…" }` ; Le porteur renvoie `method: "token"` au lieu de CSRF                                       |
| DELETE             | `/api/v1/session`                     | 204 ; révoque la session de cookie en cours ; nécessite une séance                                                                        |
| PUT                | `/api/v1/admin/password`              | `{ "current_password":"…", "password":"…" }` ; 204, révoque les autres sessions ; Le porteur révoque toutes les sessions                  |
| POST               | `/api/v1/tokens`                      | `{ "name":"automation" }` ; 201, `{ "id":"…", "name":"…", "created_at":"2026-09-29T01:02:03Z", "last_used_at":null, "token":"mwk_…" }`    |
| GET                | `/api/v1/tokens`                      | `{ "tokens":[…] }` ; métadonnées uniquement                                                                                               |
| DELETE             | `/api/v1/tokens/:id`                  | 204 ; révoque un jeton ; la suppression répétée renvoie également 204                                                                     |
| GET                | `/api/v1/mailboxes`                   | `{ "mailboxes":[…] }` ; vues de boîtes aux lettres expurgées                                                                              |
| POST               | `/api/v1/mailboxes`                   | Créez avec le formulaire de boîte aux lettres ci-dessous ; 201, vue expurgée avec `id` généré et révision 1                               |
| GET / PUT / DELETE | `/api/v1/mailboxes/:id`               | Lire/mettre à jour avec la révision actuelle/supprimer (204) ; L'ID manquant renvoie 404 `mailbox_not_found`                              |
| POST               | `/api/v1/mailboxes/test`              | Testez un formulaire non enregistré ; renvoie `{ "folders":[…] }` , conserve la configuration inchangée                                   |
| POST               | `/api/v1/mailboxes/:id/test`          | Testez le formulaire avec les informations d'identification omises et fusionnées à partir de cette boîte aux lettres                      |
| GET                | `/api/v1/mailboxes/:id/folders`       | Découvrez les dossiers sélectionnables en utilisant le budget de cette boîte aux lettres                                                  |
| GET / PUT          | `/api/v1/mailboxes/:id/subscriptions` | Lire/enregistrer les abonnements en utilisant leur propre révision ; la sauvegarde réussie renvoie 202                                    |
| GET / PUT          | `/api/v1/settings/delivery`           | Lire/enregistrer les paramètres de livraison globaux avec leur propre révision                                                            |
| DELETE             | `/api/v1/settings/delivery`           | Effacez le canal et les informations d'identification enregistrés avec `{revision}` ; 204, révision périmée 409                           |
| POST               | `/api/v1/settings/delivery/test`      | Testez le formulaire de livraison avec les identifiants enregistrés fusionnés ; 204, configuration inchangée                              |
| GET                | `/api/v1/status`                      | États des dossiers, canal, nombre de livraisons et notifications ciblées                                                                  |
| GET                | `/api/v1/diagnostics`                 | Télécharger un rapport JSON rédigé sans journaux d'exécution                                                                              |
| GET                | `/api/v1/logs`                        | Journaux opérationnels authentifiés ; Filtres `after` , `level` , `mailbox_id` , `limit` (voir ci-dessous)                                |
| GET                | `/api/v1/deliveries`                  | 50 derniers enregistrements de livraison, y compris le sujet et le résumé de la boîte aux lettres/dossier                                 |
| POST               | `/api/v1/test-push`                   | Envoyez un test en utilisant les paramètres enregistrés ; 204 une fois accepté, configuration inchangée                                   |
| POST               | `/api/v1/deliveries/:id/retry`        | Remettre en file d'attente une tâche `dead` ; 202                                                                                         |

Les enregistrements de livraison incluent un objet `message` facultatif : `mailbox_id` , `mailbox_label` , `folder` , `subject` (chaîne ou null) et `test` (booléen). Les noms sont des instantanés du temps de mise en file d’attente et survivent au renommage ou à la suppression de la boîte aux lettres. Un sujet nul signifie qu'il n'a pas été retenu ; une chaîne vide signifie que le mail n'a pas de sujet. Les notifications de test contiennent `test=true` et aucune source de boîte aux lettres. L'API omet l'expéditeur et le corps de ce résumé.

Exemple de création de boîte aux lettres :

```json
{
  "label": "Work mailbox",
  "host": "imap.example.org",
  "port": 993,
  "username": "user@example.org",
  "password": "…",
  "connection_limit": 10
}
```

L'avant-poste autorise au maximum 20 boîtes aux lettres enregistrées, y compris les configurations invalides en attente de réparation ; la création suivante renvoie 400 `mailbox_limit_exceeded` . Les identifiants sont générés sous la forme `mbx_` plus une chaîne aléatoire et restent stables. `label` utilise par défaut le nom d'utilisateur (adresse e-mail), autorise au maximum 64 caractères Unicode et rejette les sauts de ligne. Un nom d'utilisateur de plus de 64 caractères nécessite une étiquette explicite plus courte. Les étiquettes omises lors de la mise à jour conservent la valeur enregistrée ; une étiquette vide utilise le nom d'utilisateur soumis.

La boîte aux lettres PUT utilise le même formulaire plus `"revision":1` (la valeur lue dans GET ) et nécessite un hôte, un port et un nom d'utilisateur. Si l’hôte, le port et le nom d’utilisateur restent identiques, le mot de passe omis conserve la valeur enregistrée ; un mot de passe vide et explicite est rejeté. Le `connection_limit` omis conserve la valeur actuelle ; les nouvelles boîtes aux lettres sont par défaut 10, plage 2 à 100. La création de POST omet la révision ou envoie 0. Les routes de test acceptent les mêmes champs sans nécessiter de révision et conservent la configuration inchangée. Le test non enregistré nécessite des informations d'identification ; le test de boîte aux lettres enregistrée fusionne les informations d'identification omises uniquement à partir de l'ID spécifié.

Une vue GET de boîte aux lettres comprend `id` , `label` , `revision` , `host` , `port` , `username` , `connection_limit` , `connections_in_use` et `"password":{"configured":true}` . Une boîte aux lettres invalide récupérée signale `configured:false` et conserve son ID et sa révision afin de pouvoir être réparée. L'objet mot de passe GET est une vue et non une valeur d'écriture valide.

Chaque boîte mail dispose d'un budget de connexion indépendant. Les dossiers en temps réel consomment chacun un emplacement ; tous ses dossiers planifiés en consomment un. Leur demande combinée doit correspondre à `connection_limit - 1` , en réservant un emplacement pour la gestion. L'abonnement PUT et les modifications de limite de boîte aux lettres appliquent cette formule ; la capacité épuisée renvoie `connection_budget_exceeded` . Le test d'une boîte aux lettres non enregistrée utilise un budget temporaire. Les points de terminaison de test non enregistré, de test de boîte aux lettres enregistrée et de découverte de dossier partagent en outre une limite de concurrence globale de 2 ; les demandes excédentaires renvoient immédiatement 429 `discovery_busy`. L'achèvement ou l'annulation libère l'admission.

Exemple de livraison PUT pour une voie initialement non configurée :

```json
{
  "revision": 0,
  "channel": "bark",
  "preview": "off",
  "retry_count": 0,
  "language": "en",
  "bark": { "endpoint": "https://api.day.app", "key": "…" }
}
```

Lisez `/settings/delivery` avant chaque mise à jour et envoyez sa révision ; la première sauvegarde avance de 0 à 1. Pushover utilise `"pushover":{"token":"…","user":"…"}` ; Le webhook utilise `"webhook":{"url":"https://hooks.example.org/mail","secret":"…"}` . La chaîne, l'aperçu et la révision sont requis. La langue omise et le nombre de tentatives utilisent les valeurs par défaut. Si la destination reste identique, les identifiants omis, y compris l’URL Webhook, conservent les valeurs enregistrées ; un identifiant actif vide explicite est rejeté. GET remplace chaque identifiant par `{"configured":true|false}` . Le point final de test distinct ne nécessite pas de révision.

Les créations de boîtes aux lettres et les modifications de connexion testent la connexion/connexion avant d'enregistrer. Si l'hôte, le port, le nom d'utilisateur et la limite de connexion correspondent aux valeurs enregistrées et que le mot de passe est omis, PUT modifie uniquement l'étiquette : la vérification des révisions et la persistance s'appliquent toujours, tandis que les tests et le remplacement de l'observateur sont ignorés. Les tâches en file d'attente conservent leur étiquette d'origine ; les tâches futures utilisent la nouvelle étiquette. La création ou la mise à jour d'une identité déjà utilisée par une autre boîte aux lettres renvoie 409 `mailbox_duplicate` (l'hôte et le nom d'utilisateur ne respectent pas la casse, le port est exact) ; la création simultanée ne peut enregistrer cette identité qu'une seule fois. Livraison PUT enregistre tous les canaux après le champ, l'adresse, l'état d'appairage sélectionné et la validation de la révision, sans envoyer de test ni contacter un fournisseur. Les tests de livraison explicites laissent les paramètres enregistrés inchangés ; leur échec ne bloque pas une sauvegarde ultérieure. Les tests de boîte aux lettres s'exécutent en dehors des verrous de configuration et maintiennent les observateurs existants actifs ; un test de boîte aux lettres ayant échoué conserve sa configuration précédente. Les révisions des paramètres obsolètes renvoient 409 `settings_conflict` ; une modification d’abonnement lors d’un test de boîte aux lettres invalide également l’instantané de ce test. Rechargez avant de réessayer. Chaque boîte aux lettres et les paramètres de remise globaux ont des révisions indépendantes, de sorte que les modifications apportées à différentes boîtes aux lettres ou à une boîte aux lettres et la livraison n'entrent pas en conflit.

Un PUT réussi renvoie la vue expurgée et s'applique immédiatement. Un changement de connexion à la boîte aux lettres arrête ses propres observateurs avant la sauvegarde et la réinitialisation facultative du curseur ; une sauvegarde ratée les restaure. Les autres boîtes aux lettres continuent de fonctionner. Les modifications de livraison préservent l’instantané d’une tentative en vol ; les tentatives ultérieures utilisent le nouveau. Les tâches en attente attendent pendant que la livraison n'est pas configurée.

`GET /api/v1/mailboxes/:id/subscriptions` renvoie `{"revision":1,"folders":[{"name":"INBOX","check":"realtime"},{"name":"Clients","check":"5m"}]}` . PUT envoie la révision lue et le tableau `folders` complet ; un tableau vide se désabonne de tout. Les noms sont uniques dans chaque boîte aux lettres, INBOX est normalisé et chaque nom fait au maximum 4 096 octets UTF-8. Chaque dossier nécessite `name` et `check` ; le chèque est `realtime`, `5m` ou `15m`. Les vérifications manquantes/invalides et le format de tableau de chaînes précédent renvoient 400. La formule de demande de connexion ci-dessus remplace la limite du nombre de dossiers. La modification de la seule vérification préserve la progression, rattrape les nouveaux messages et conserve les ID d'événement existants ; l'annulation et la réinscription ultérieure créent une nouvelle base de référence. Tous les champs sont obligatoires. L'enregistrement renvoie 202 avec la prochaine révision ; les révisions obsolètes renvoient 409 `subscriptions_conflict` . L'existence du dossier est confirmée par l'observateur, de sorte que les abonnements peuvent être supprimés lorsque la boîte aux lettres est hors ligne. Les échecs de stockage renvoient 503 et préservent les abonnements en cours.

Chaque article `/status.folders[]` comprend `mailbox_id`, `mailbox_label` et `check`. `mode` est `idle` , `poll` ou `scheduled` ; Les dossiers planifiés incluent également `next_check` (heure RFC 3339, y compris les délais de nouvelle tentative). Les dossiers en temps réel omettent `next_check` . La configuration enregistrée qui échoue JSON ou la validation du modèle n'est plus configurée, avec `configuration_invalid` dans `/status.notices` sous `mailbox:<id>` ou `delivery` ; PUT répare cet article. Les abonnements excédentaires suspendent uniquement cette boîte aux lettres et signalent `connection_budget_exceeded` sous `subscriptions:<id>` . Réduire les abonnements ou augmenter sa limite reprend la surveillance. Les échecs de déchiffrement arrêtent le démarrage.

La liste d'autorisation de diagnostic contient l'heure de génération, les informations de build/Go/plateforme, le nombre de files d'attente et les états de boîte aux lettres anonymes et d'observateur, notamment `check` et `mode` . `mailboxes[].index` et `watches[].mailbox_index` identifient « Boîte aux lettres 1 », « Boîte aux lettres 2 », etc. dans ce rapport ; les observateurs de chaque boîte aux lettres ont leur propre `index`. Les entrées de boîte aux lettres incluent les codes de notification de configuration/abonnement. Seul `imap_folder_rejected` peut inclure `watches[].response_code` . Le rapport omet les ID de boîte aux lettres, les noms d'affichage, les adresses, les noms de serveur, les noms de dossiers, les données de message, les ID de tâche, les informations d'identification et tous les autres paramètres d'erreur.

États de la tâche : `pending` attend une demande, `accepted` signifie que le canal a accepté la demande, `dead` signifie que la tentative s'est terminée par un échec enregistré.

## Journaux d'exécution

Utilisez `docker compose logs -f core` (ou `docker logs <container>` ) pour la sortie JSON stderr. Les clients authentifiés peuvent interroger les mêmes événements opérationnels via `GET /api/v1/logs?after=0&level=info&mailbox_id=mbx_example&limit=200`. Un cookie de session ou un jeton API est requis.

L'anneau en mémoire conserve les **2000** entrées d'information/avertissement/erreur les plus récentes et s'efface au redémarrage. `after` est une séquence non négative exclusive ; `level` sélectionne ce niveau et au-dessus (par défaut `info`) ; Le `mailbox_id` omis inclut toutes les boîtes aux lettres et opérations globales. La valeur par défaut de `limit` est 200, plage 1 à 500. Les paramètres non valides renvoient 400 `logs_request_invalid` .

```json
{
  "entries": [
    {
      "seq": 42,
      "time": "2026-09-29T00:00:00Z",
      "level": "info",
      "message": "Folder connection established",
      "attrs": {
        "mailbox_id": "mbx_example",
        "folder": "INBOX",
        "mode": "idle",
        "check": "realtime"
      }
    }
  ],
  "next": 42,
  "cleared_through": 0
}
```

Utilisez `next` comme `after` de la prochaine demande. À la limite de la page, c'est la dernière séquence renvoyée ; après avoir analysé l'instantané, il avance jusqu'à la fin de l'instantané, y compris lorsque les filtres ne correspondent à rien. Les entrées expulsées sont ignorées. Si le redémarrage renvoie `next < after` , réinitialisez le curseur à 0. Les entrées sont classées par séquence.

Les journaux couvrent les cycles de vie des boîtes aux lettres et des dossiers, le nombre de références/mises en file d'attente, les tentatives de livraison, les modifications de configuration et les actions de l'administrateur. Les événements globaux utilisent un `mailbox_id` vide. Seuls ces attributs scalaires sont exposés : `mailbox_id` , `folder` , `mode` , `check` , `code` , `event_id` , `channel` , `attempt` , `duration_ms` , `http_status` , `next_attempt` , `backoff_seconds` , `count` , `revision` , `token_id` , `version` , `listen` . `http_status=0` signifie qu'aucune réponse HTTP n'a été reçue ; `token_id` est un identifiant d'enregistrement public, et non la valeur du jeton. Les mots de passe, les valeurs de jeton/session, les codes de configuration, l'expéditeur, le sujet, les corps et les URL d'identification sont omis. La ligne de code de configuration va uniquement à la console. Les journaux contiennent des noms de dossiers ; l'exportation de diagnostic anonyme exclut les journaux et les noms.

Les tests de découverte de dossiers et de boîte aux lettres d'administrateur renvoient `folders` comme tableau de chemin d'origine plus `folder_roles` en option, une carte du chemin vers `inbox`, `drafts`, `sent`, `trash`, `junk`, `archive`, `all` ou `flagged` . Les rôles proviennent des attributs IMAP SPECIAL-USE ; INBOX est reconnu par son nom défini par le protocole. L'interface utilisateur Web utilise des correspondances exactes, insensibles à la casse, pour les noms de dossiers anglais courants lorsque les rôles sont absents. Les noms traduits affichent le chemin d'origine entre parenthèses ; les autres noms restent inchangés. Afficher les traductions ne modifie jamais les chemins d’abonnement. Les titres des notifications utilisent le segment final des chemins de dossiers séparés par des barres obliques ; les champs du dossier structuré conservent le chemin complet. Les tests de boîte aux lettres d'application continuent de renvoyer uniquement l'état de connexion.

`GET /api/v1/session` inclut `username` pour une session de cookie authentifiée. L'authentification par jeton conserve sa réponse existante.

### Appareil de réception d'applications

Les paramètres de livraison incluent `native_pairing_id` . Pour le canal `native`, sélectionnez un appairage actif parmi `/api/v1/native/devices` . Les notifications de test et les nouveaux événements ciblent uniquement ce appairage. L'omission du champ lors d'une mise à jour préserve la sélection enregistrée ; une sélection vide renvoie `native_target_required` . Une sélection indisponible renvoie `native_target_unavailable` . La révocation de l’appairage natif sélectionné efface atomiquement le canal et native_pairing_id et incrémente la révision une fois. Les autres cibles et canaux restent intacts. Les livraisons existantes conservent leur cible et leur résultat de révocation. Voir [synchronisation de l’appairage](https://github.com/mingzaily/mailwake/blob/main/docs/native-push.md#pairing-revocation-synchronization) pour connaître le comportement de synchronisation, de concurrence et de redémarrage.

L'autorisation des applications gère l'administration et l'autorisation push de manière indépendante. La révocation de l’une ou l’autre autorisation laisse l’autre inchangée.

### Notifications de tests natifs

L'avant-poste utilise `POST /v1/pairing-tests` signé avec `{pairing_id,test_id}` (un nouvel UUID par test explicite). Le service de transmission des notifications génère du contenu fixe et le soumet de manière synchrone à APNs sans nécessiter le droit Native Push. Les tests de formulaire, les tests de paramètres enregistrés dans l'application et `/api/v1/test-push` utilisent ce même chemin. Les tests sont distincts de la boîte d'envoi du courrier et ne comportent aucune tentative de livraison automatique.

L'avant-poste renvoie HTTP 204 uniquement lorsque le service de transmission des notifications signale `accepted` . Rejet de APNs, résultats inconnus et `sending` renvoient une erreur localisée ; la limitation du débit du service de transmission des notifications fait que l'avant-poste renvoie HTTP 429 avec `Retry-After` . Les journaux sécurisés incluent l'état HTTP du service de transmission des notifications et le code d'erreur stable, à l'exclusion des corps de réponse, des jetons et du contenu des notifications. L’avant-poste et l’application partagent les quotas quotidiens par minute et par sujet existants du service de transmission des notifications ainsi que l’idempotence `(device_id,test_id)`. Un utilisateur démarrant un autre test crée un nouvel ID de test.

Le courrier formel utilise `/v1/push` et reste limité aux droits, quels que soient l'ID d'événement ou les indicateurs de test client. Déployer l'itinéraire de test de appairage du service de transmission des notifications avant cette version d'avant-poste ; l'avant-poste ne recourt pas à la livraison du courrier payante.

## Effacer l'historique de livraison et les journaux d'exécution

- `DELETE /api/v1/deliveries` renvoie `200 {"deleted":N}` . Il supprime définitivement les enregistrements terminés ( `accepted` / `dead` ) dans l'historique complet, y compris leurs enregistrements natifs associés. Les livraisons/tentatives en attente, les enregistrements natifs toujours en attente/en file d'attente/envoi ou planifiés pour des contrôles de statut et les mises à jour d'activité native en attente sont conservés. La suppression des échecs supprime leur option de nouvelle tentative manuelle. Les points de contrôle et la configuration de la boîte aux lettres sont conservés.
- `DELETE /api/v1/logs` renvoie `200 {"cleared_through":N}` et efface l'anneau en mémoire partagé pour tous les niveaux et boîtes aux lettres. Les numéros de séquence continuent d'augmenter ; Les journaux stderr/container et les fichiers exportés restent. `GET /api/v1/logs` renvoie également `cleared_through` ; les clients suppriment les entrées mises en cache avec `seq <= cleared_through` avant d'ajouter les entrées renvoyées.
- Les deux points de terminaison nécessitent une session d'administrateur + Origin/CSRF ou un jeton d'API d'administrateur. Les informations d’identification de l’appareil d’application n’accordent pas ces opérations. Un nettoyage répété est sûr ; de nouveaux travaux et entrées de journal peuvent apparaître après l'opération.

## Contenu des messages de l’application

`POST /api/v1/app/content` utilise un identifiant de contrôleur lié à l’appareil et l’autorisation `content` accordée par le propriétaire. Envoyez `{ "reference": <message_ref> }` ; la réponse contient `text`, `truncated`, éventuellement un `html` assaini et `has_remote_images`. La lecture est gratuite.

Une fois le corps lisible, `POST /api/v1/app/content/read`, avec les mêmes identifiants, autorisation et référence, marque le message comme lu dans la boîte mail. Le succès renvoie `204 No Content` et `Cache-Control: no-store`. La seule récupération du contenu conserve l’état de lecture. Les deux opérations vérifient l’autorisation actuelle, le dossier suivi, l’identité de la boîte mail et la référence du message.

## Destination des identifiants

Lors des tests et de l’enregistrement, modifier `host`, `port` ou `username` exige de saisir à nouveau le mot de passe enregistré ; sinon, la réponse est 400 `mailbox_password_required_for_change`. Modifier l’adresse Bark enregistrée exige la clé de l’appareil, même si un autre canal est sélectionné ; sinon, la réponse est 400 `bark_key_required_for_change`. Le port, le chemin et la barre finale font partie de l’adresse. Une requête rejetée n’ouvre aucune connexion et conserve la configuration et la révision.

## Diagnostic des requêtes App

Les exports utilisent `schema_version: 2`. `app_requests` contient au maximum 300 requêtes App récentes et terminées, issues du journal partagé de 2000 événements en mémoire : heure, `request_id` généré par le serveur (également dans `X-Request-ID`), modèle de route, méthode, statut HTTP, durée, étape, résultat et codes sûrs. Les identifiants de ressources, paramètres de requête, secrets, références signées, corps des messages et journaux complets sont exclus. Un redémarrage ou un effacement des journaux supprime ces entrées ; les autres événements peuvent évincer les plus anciennes. Le téléchargement actualise le rapport. Les requêtes qui ne parviennent pas au serveur nécessitent les diagnostics App.

`POST /api/v1/app/content/read` vérifie UID et indicateurs. Un message déjà lu réussit ; un message non lu est marqué puis vérifié à nouveau. STORE OK seul ne prouve pas son existence. UID absent, UIDVALIDITY modifié ou dossier explicitement absent donnent 404 ; refus du fournisseur ou de permission, 503 ; délai dépassé, 504. Le corps en cache reste lisible si la référence distante expire.
