<a id="haut"></a>

# My Amana App — Reste à faire API et registre des anomalies

**Document confidentiel · annexe technique au rapport de direction**

| | |
|---|---|
| **Client** | Amana Assurances |
| **Projet** | Application mobile « My Amana » |
| **Prestataire** | SHIFTIN — accompagnement technique OCTERA |
| **Date d’édition** | 16 septembre 2026 (édition initiale : 15 septembre) |
| **Environnement de référence** | `https://gateway.amana.dz/MOBILE/api/v1` — spécification en ligne v4, **94 chemins** |
| **Vérifications** | Relevé automatisé du 15/09/2026 · **campagne de tests en écriture menée en conditions réelles le 16/09/2026** |
| **Document parent** | [`Board_Report_API_Integration.md`](./Board_Report_API_Integration.md) |

> Ce document répond à deux questions : **que reste-t-il à faire côté API pour finaliser
> l’intégration sans réserve**, et **quels problèmes affectent aujourd’hui les endpoints
> qui ne sont pas encore intégrés**. Il est exploitable tel quel par l’équipe backend :
> chaque point porte l’endpoint concerné, le comportement observé, le comportement attendu
> et l’impact applicatif.
>
> ⚠️ **Changement majeur depuis l’édition du 15/09.** Le travail de câblage côté
> application est **terminé** : plus aucun écran ne fonctionne sur des données simulées.
> Les endpoints d’écriture ont été **exercés pour de vrai** plutôt que codés d’après la
> documentation — cinq se sont révélés mal documentés au point de rendre le code
> inutilisable, et la passerelle refuse par ailleurs toute pièce jointe de plus d’un
> mégaoctet. Le reste à faire est désormais **presque entièrement backend**.

---

## Sommaire

1. [Tableau de bord](#t1)
2. [Ce qui reste à faire côté API — travaux backend](#t2)
3. [Ce qui reste à faire côté application](#t3)
4. [Registre exhaustif des anomalies](#t4)
5. [Endpoints intégrés portant une anomalie résiduelle](#t5)
6. [Validations de bout en bout jamais exécutées](#t6)
7. [Ordre de résorption recommandé](#t7)
8. [Annexe — reproduction et outillage](#t8)

---

<a id="t1"></a>

## 1. Tableau de bord

[↑ Sommaire](#haut)

| Indicateur | 15/09 | 16/09 | **17/09** |
|---|---|---|---|
| Chemins déclarés dans la spécification en ligne | 94 | 94 | **94** |
| Chemins réellement appelés par l’application | 45 | 62 | **62** |
| Flux de données applicatifs | 74 | 82 | **82** |
| dont branchés sur l’API réelle | 60 | 82 | **82** |
| dont encore simulés | 14 | 0 | **0** |
| Demandes backend ouvertes | 23 | 38 | **39** |
| dont bloquantes | 4 | 12 | **13** |
| Tunnels de souscription validés de bout en bout, paiement réel compris | 0 | 0 | **4 / 4** |

**Le 17/09 change la nature du tableau.** Les quatre tunnels — Voyage, Scolaire,
Accident, Santé — règlent désormais un **vrai paiement par carte** et atteignent
l'écran de succès, en exécution automatisée et enregistrée. La réserve qui
pesait depuis juillet est levée. Le nombre de demandes ne bouge pas pour autant :
P0-A se ferme, mais deux demandes s'ouvrent à sa place : la passerelle ne ramène
pas dans l'application (P0-M), et elle nie le paiement qu'elle vient d'encaisser
(P0-N). Aller au bout du parcours n'a pas simplifié le registre — cela a remplacé
une réserve vague par deux défauts nommés et reproductibles.

Le nombre de demandes backend **augmente** alors que le travail applicatif s’achève :
c’est le résultat direct de la campagne de tests en écriture. Dix anomalies nouvelles
ont été mises au jour, dont cinq bloquantes, qui seraient autrement apparues en recette
ou en production.

---

<a id="t2"></a>

## 2. Ce qui reste à faire côté API — travaux backend

[↑ Sommaire](#haut)

### 2.1 🔴 P0 — Bloquants pour une mise en production

- [x] **P0-A · ✅ RÉSOLU le 17/09 — environnement de paiement par carte.**
      Les cartes de test transmises le 09/09 fonctionnent. Les quatre tunnels ont été
      joués de bout en bout en automatisé : formulaire de carte, 3-D Secure, règlement
      encaissé, `confirmPayment`, écran de succès. La demande ouverte depuis juillet est
      close. *Ce qui reste du paiement est suivi en P0-M ci-dessous.*

- [ ] **P0-M · 🆕 Revenir dans l'application après le 3-D Secure.**
      Le règlement aboutit, puis la passerelle redirige vers **`site-test.amana.dz`**,
      le site vitrine d'Amana, au lieu du lien profond de l'application. Le client reste
      dans le navigateur, sur une page sans rapport avec sa souscription, et doit fermer
      la session à la main pour revenir. Vérifié le 17/09 sur des paiements réellement
      encaissés, sur les quatre produits. **Attendu** : que l'`returnUrl` transmise à
      Satim soit le lien profond de l'application. **Impact** : le parcours de paiement
      se termine sur une impasse visuelle — c'est le dernier obstacle entre un paiement
      qui marche et un paiement présentable. (§30)

- [ ] **P0-N · 🆕 `confirmPayment` nie le paiement qu'il vient d'encaisser.**
      Au retour du 3-D Secure, l'endpoint répond « non valide » sur un contrat dont le
      statut est pourtant passé à `ENCAISSE` et dont la police est émise. Vérifié le
      17/09 sur les quatre produits, paiements réels. **Contournement livré** :
      l'application relit le contrat et se fie à son statut plutôt qu'à la réponse de
      la passerelle — d'où un écran de succès qui met jusqu'à **quatre-vingt-dix
      secondes** à s'afficher. **Attendu** : que la réponse reflète l'encaissement.
      **Impact** : sans le contournement, tout paiement réussi s'afficherait en échec.
      (§29)

- [ ] **P0-B · `GET /points-verts/paiement/{quittanceId}` répond `500`.** Observé le
      20/07, toujours reproduit au 16/09. **Attendu** : `404` sur quittance inconnue,
      `400` sur solde insuffisant, et une méthode `POST` pour une opération qui modifie
      l’état.

- [ ] **P0-C · Exposer la confirmation du paiement à la livraison.** L’endpoint
      `confirmPayment/livraison/{quittanceId}` décrit par l’équipe backend le 30/07
      **n’existe pas dans la spécification**. L’application ne peut pas confirmer le
      règlement d’un contrat payé à la livraison.

- [ ] **P0-D · Vérifier l’OTP téléphone à l’inscription.** Le backend ne contrôle que
      l’OTP e-mail. **Impact** : un compte peut être créé avec un numéro dont
      l’utilisateur n’est pas titulaire.

- [ ] **P0-L · 🆕 Servir `GET /documents` à tout souscripteur.** L'endpoint répond
      `500` dès que l'appelant n'est pas adhérent santé : il passe par
      `bulletinV2DocumentUtil/{adherentId}` et meurt quand la recherche échoue
      (« cet adherent n'existe pas »). Or il sert un **catalogue de textes
      réglementaires publics**, sans rapport avec la santé. Découvert le 16/09 en
      exécutant les parcours E2E. **Impact** : écran mort pour la majorité des
      souscripteurs, là où la réglementation impose la mise à disposition.
      **Attendu** : servir le catalogue à tout compte authentifié, et remonter un
      `404` amont en `404` plutôt qu'emballé dans un `500`. (§32)

- [ ] **P0-F · 🆕 Documenter le contrat réel de `PUT /members`.** L’endpoint est un
      **remplacement d’enregistrement complet**, pas une mise à jour partielle ; son bloc
      questionnaire s’appelle `questionnaire` et non `bonneSanteQuestion` ; ses réponses
      sont les chaînes « Oui »/« Non » et non des booléens. Sa validation
      (téléphone 10 chiffres, code postal 5 chiffres, ville et date d’entrée en service
      obligatoires) n’est documentée nulle part. **Impact** : codé d’après la
      documentation, l’enregistrement du profil santé échouait à 100 %.

- [ ] **P0-G · 🆕 Annoncer les renommages de champ.** Le champ portant l’acte de
      `POST /refunds` est passé de `type` à `codeActe` côté serveur **sans préavis** ;
      l’ancien nom répond `404 « Acte non trouvé »`. Un contrat consommé par une autre
      équipe ne peut pas changer silencieusement.

- [ ] **P0-H · 🆕 Exposer une URL téléchargeable pour les documents utiles.**
      `GET /documents` renvoie un chemin de fichier serveur
      (`opt/amana-digitale-preprod/…`) ; `GET /assets?path=` répond `404`. La liste est
      affichée mais **aucun document n’est ouvrable**.

- [ ] **P0-I · 🆕 Relever la limite de téléversement.** La passerelle refuse tout
      fichier de plus d’environ **1 Mio** (valeur par défaut de Spring, laissée telle
      quelle) et répond `500 « Maximum upload size exceeded »`, sans nom de champ ni
      indication de la limite. Une photo de justificatif prise avec un téléphone récent
      pèse 3 à 5 Mo : **le cas nominal de la fonction ne passe pas**. Mesuré le 16/09 :
      1 024 Kio créé, 1 200 Kio refusé ; le plafond est par fichier et non par requête.
      **Attendu** : `max-file-size` porté à une valeur réaliste (10 Mo), et un `413` — ou
      un `422` nommant la pièce — portant la limite dans le corps.

- [ ] **P0-J · 🆕 Publier le contrat de `POST /members/addPieceJointe`.** Annoncé prêt
      le 09/08/2026, l’endpoint répond — mais **rien n’indique ce qu’il attend** : le spec
      déclare un corps multipart **sans aucun champ**, et toute requête répond
      `422 « The given data was invalid. »` **sans nommer le moindre champ**. Plus de
      25 noms de parties ont été testés le 16/09, en fichier comme en texte : même réponse.
      Une seule propriété a pu être identifiée, `rib` (de type `String`), et uniquement
      parce qu’une erreur de conversion l’a laissée fuiter. **Le dépôt des pièces du profil
      santé n’est donc pas branchable** — ce n’est pas un reste applicatif.
      **Attendu** : la liste des parties attendues (un `curl` fonctionnel suffirait), ou à
      défaut un validateur qui nomme les champs manquants, comme le fait `/refunds`.

- [ ] **P0-K · 🆕 Exposer le référentiel `produit` de `POST /claims`.** La déclaration de
      sinistre **n'est jamais déposée** : l'écran collecte garantie, date, circonstances et
      pièces, puis affiche un écran de succès sans rien envoyer. L'endpoint existe et son
      contrat a été reconstitué le 16/09 — sauf `produit`, qui doit correspondre à un
      référentiel « produit sinistre » **qu'aucun endpoint mobile n'expose**. Dix valeurs
      candidates testées (codes produit, identifiants, libellés) : toutes échouent sur
      `Attempt to read property "produit_sinistre_id" on null`.
      **Attendu** : le référentiel, ou la liste des valeurs acceptées.
      ⚠️ **Piège associé** : le validateur nomme les champs `date_naiss`, `date_sinistre`
      et `path_files`, alors que l'endpoint lit `dateNaiss`, `dateSinistre` et `pieces`.
      Envoyer exactement ce que l'erreur réclame renvoie la même erreur.

### 2.2 🟠 P1 — Défauts de fiabilité de contrat

- [ ] **P1-A · Ne plus répondre « succès » sur un échec** *(V8b, ouvert depuis le 18/08)*.
      `POST /souscriptions/voyage` renvoie `201 success: true` avec un `contratId` qui
      répond ensuite `404`. **Contournement** : relecture systématique du contrat après
      chaque souscription.
- [ ] **P1-B · Rendre la souscription Voyage atomique** *(V8c)*. Trois appels enchaînés
      sans transaction ni annulation.
- [ ] **P1-C · Valider réellement les champs obligatoires** *(V5)*. Sur
      `POST /simulation-voyage`, omettre `age` renvoie `200` avec un **prix plausible mais
      faux**.
- [ ] **P1-D · Trancher le plafonnement de la prime Famille au 7ᵉ voyageur** *(V13)*.
      Seul point ouvert à impact financier direct.
- [ ] **P1-E · Corriger les primes négatives** renvoyées en succès par la convention
      tarifaire appliquée au type Groupe *(V12)*.
- [ ] **P1-F · 🆕 Documenter la règle des 100 % sur `PUT /beneficiaries/updateTaux`**, et
      **seeder une répartition cohérente sur le compte de test** : il porte aujourd’hui
      10 % + 19 %, donc aucune répartition ne peut y être enregistrée.
- [ ] **P1-H · 🆕 Rendre exploitable le devis de renouvellement Santé.**
      `POST /avenants/{contratId}/renouvellements/sante/devis` répond
      `409 « problem devis Renouvellement Sante »` à **tout** payload — six contrats testés
      le 16/09 (individuel et famille, encaissés et non payés), les deux valeurs de
      `modifyDevis`, `typecontratid` 530 et 531, `formule` en libellé et en identifiant.
      Aucun champ nommé, aucune raison donnée. **Attendu** : un message exploitable, ou les
      préconditions par écrit (états de contrat renouvelables, fenêtre de renouvellement).
- [ ] **P1-I · 🆕 Corriger le DTO d’erreur de l’adjonction Santé.**
      `POST /avenants/{contratId}/adjonctions/sante/devis` type son champ `error` en `int`
      alors que le service amont renvoie la chaîne `"409 :"`. La passerelle plante donc sur
      sa propre gestion d’erreur et répond `500`, masquant le refus métier réel.
      **Attendu** : corriger le type, puis expliciter la cause du `409`.
- [ ] **P1-G · 🆕 Publier les énumérations `LienParenteEnum` et `SexeEnum`.** La première
      n’accepte que `Conjoint`, `Enfant` et `Ascendant` — le sélecteur de l’application
      propose aussi « frère/sœur » et « autre », qui n’ont **aucune représentation
      possible**. La seconde n’accepte que `Masculin` / `Féminin`.

### 2.3 🟡 P2 — Alignement du contrat et de la documentation

- [ ] **P2.1 · `GET /auth/me`** — DTO plat déclaré, réponse imbriquée, `nom`/`prenom`
      vides à la racine.
- [ ] **P2.2 · Enveloppe d’erreur et codes `419` / `420`** non déclarés.
- [ ] **P2.3 · Cinq formats de dates coexistent.** À quoi s’ajoute désormais le cas de
      `/beneficiaries`, qui **renvoie** `JJ/MM/AAAA` et **refuse** ce même format en
      écriture.
- [ ] **P2.4 · Deux enveloppes de pagination subsistent** (page complète / page réduite).
- [ ] **P2.5 · `GET /contrats` n’expose aucun numéro de police.**
- [ ] **P2.6 · Les téléversements ne sont pas déclarés `multipart/form-data`**, et leurs
      champs ne sont documentés nulle part — ni pour `/refunds`, ni pour `/nousContacter`,
      ni pour la suppression d’un ayant droit.
- [ ] **P2.7 · Types et casse divergents** (`personne_id` chaîne vs `personneId` entier,
      `/destination/getGlobal`, `/auth/refresh`).
- [ ] **P2.8 · Documenter les énumérations** déclarées « texte libre ». S’y ajoutent
      désormais `statut` de la carte Flash (qui vaut en réalité `"false"`), et les
      réponses « Oui »/« Non » du questionnaire médical.
- [ ] **P2.9 · Fournir un exemple de requête fonctionnelle par endpoint d’écriture.**
      Demande du 27/07/2026, restée sans réponse. La campagne du 16/09 confirme que c’est
      la mesure au meilleur rapport coût/effet du registre.
- [ ] **P1-J · 🆕 Signaler un compte sans enregistrement souscripteur.**
      ⚠️ **Rectification.** Ce point annonçait d'abord que `/auth/me` n'exposait
      jamais l'identité du souscripteur. C'était faux, et l'erreur venait d'un
      seul compte testé. `test-chahinaz` renvoie tout — `personneId`, bloc
      `souscripteur`, date de naissance, adresse, wilaya. `khaddoucheTest` ne
      renvoie rien de tout cela. **Conséquence** : la souscription est
      impossible avec ce compte, `POST /souscriptions` exigeant `personneId` ;
      le client remplit tout le tunnel et n'obtient que « Paiement non abouti »,
      alors que rien n'a été créé et qu'aucun paiement n'a été tenté.
      **Attendu** : refuser l'entrée dans le tunnel avec un motif nommé, plutôt
      qu'un échec en fin de parcours. (§33)

- [ ] **P2.13 · 🆕 Documenter le format de date de `POST /beneficiaries`.** Le seul
      format accepté est la forme compacte `AAAAMMJJ` (`19900101`), documentée nulle
      part : le format annoncé `JJ/MM/AAAA` répond `400`, et toutes les formes ISO
      `422`. ⚠️ **Rectification d’un constat de ce registre** : l’endpoint avait été
      classé inopérant (ancien P0-E, référence retirée et non réattribuée) ; il
      est fonctionnel, et l’application le
      consomme depuis le 16/09 — création puis suppression du bénéficiaire 110
      vérifiées en live. Il reste une demande de documentation, pas un blocage.

- [ ] **P2.12 · 🆕 Honorer ou retirer le champ `declare` de `POST /members/validation`.**
      `declare: false` produit le même message de succès et le même effet que
      `declare: true` (vérifié en live). Le drapeau n’est pas lu.
      ⚠️ Par ailleurs, le message de succès demande à l’assuré de télécharger son bulletin
      d’adhésion puis de le renvoyer signé — or `GET /members/bulletinAdhesion` répond
      `500` (P3.10) et `POST /members/addPieceJointe` est inutilisable (P0-J).
      **L’étape que le message réclame est aujourd’hui impossible de bout en bout.**
- [ ] **P2.11 · 🆕 Accepter un `professionId` sur `POST /personnes/assure`.** Le champ
      `profession` est une chaîne libre : l’application envoie le libellé choisi dans le
      référentiel. Un identifiant supprimerait cet aller-retour. Trier `GET /professions`
      côté serveur éviterait par ailleurs au client de le faire (les 354 lignes arrivent
      dans l’ordre d’insertion).
- [ ] **P2.10 · 🆕 Harmoniser les enveloppes de réponse en écriture.** `/nousContacter`
      renvoie `{error_code, message, id}`, `/refunds` renvoie
      `{status, timestamp, message, path, details}`, `/members` renvoie `{message}`,
      `/chat/send` renvoie `{conversationId, message}` — quatre formes pour quatre
      endpoints.

### 2.4 ⬜ P3 — Endpoints manquants ou insuffisants

- [ ] **P3.1 · `GET /actes/groups`** — ni `category`, ni illustration par acte, et
      **`codeActe` n’est pas unique** (les 8 prestations sociales partagent `Evenement`).
- [ ] **P3.2 · `GET /notifications/unread-count` répond `404`** — le badge est compté
      côté application sur la première page de la liste.
- [ ] **P3.3 · Aucun code promotionnel valide en base.**
- [ ] **P3.4 · `GET /contrats/{id}/documents` répond `404`** même avec un identifiant réel.
- [ ] **P3.5 · Le détail de contrat est incomplet** : personnes assurées, libellé du moyen
      de paiement et valeur par garantie sans source.
- [ ] **P3.6 · `GET /garanties/{codeProduit}` renvoie des valeurs par formule** là où le
      modèle attend une valeur unique.
- [ ] **P3.7 · `/auth/password-policy` annoncé le 12/08 mais absent de la spécification.**
- [ ] **P3.8 · `GET /assets` sans `path`** → `500` puis `404`, jamais `400`.
- [ ] **P3.9 · `GET /tarifs` sans `codeProduit`** → `200 []` au lieu de `400`.
- [ ] **P3.10 · `GET /members/bulletinAdhesion`** → `500` et pas de réponse au-delà de 12 s.
- [ ] **P3.11 · `GET /refunds/{id}/documents`** — régression `200` → `404` depuis le 03/08.
- [ ] **P3.12 · 🔴 Rattacher le chat du service client à un sinistre.** `GET /chat/history`
      et `POST /chat/send` n’exposent **aucun paramètre de dossier**, et tous les messages
      du compte de test partagent un identifiant de conversation unique. Le chat a été
      branché le 16/09 à la demande : **la même conversation s’affiche donc sous chaque
      sinistre**, et un message envoyé depuis un dossier apparaît sous tous les autres.
      **Attendu** : un paramètre `sinistreId`, ou une décision produit actant un fil
      unique — qui devrait alors être déplacé du sinistre vers le menu.
- [ ] **P3.13 · Confirmer l’absence de niveau Groupe pour la Santé** (types 530 et 531
      seulement).
- [ ] **P3.14 · Fournir un indicateur de formule recommandée** (badge « Meilleure
      formule »).
- [ ] **P3.15 · Confirmer l’ordre de tri et la politique d’exclusion de `GET /contrats`.**
- [ ] **P3.16 · Localiser le contenu éditorial.** `/a-propos-amana/sos`, `/documents` et
      `/actes/groups` ignorent `accept-language` : en arabe, l’application affiche des
      titres traduits autour de contenus français.
- [ ] **P3.17 · 🆕 Accepter un envoi multipart pour le justificatif de résiliation.**
      `AvenantDTO.justificatif` est une chaîne base64, là où tous les autres
      téléversements de l’API sont multipart. **Conséquence** : la demande de résiliation
      part avec son motif et son commentaire, **mais sans ses pièces jointes** — les
      produire exigerait une nouvelle dépendance native côté mobile.
- [ ] **P3.18 · 🆕 Publier la liste des `motifId` de résiliation**, ou exposer un
      `GET /avenant/motifs`. Le champ est un entier sans énumération documentée.
- [ ] **P3.19 · 🆕 Exposer l’état d’une demande de retrait d’ayant droit.**
      `/ayantDroits` continue de renvoyer l’ayant droit après la demande, sans aucun
      indicateur : l’état « en attente » est donc maintenu localement sur l’appareil.

---

<a id="t3"></a>

## 3. Ce qui reste à faire côté application

[↑ Sommaire](#haut)

**Le câblage est terminé.** Les 82 flux de données consomment l’API réelle. Ce qui suit
est le reliquat, et chaque ligne dépend d’une correction backend listée ci-dessus.

| # | Reste à faire | Bloqué par |
|---|---|---|
| 1 | Brancher le dépôt des pièces du profil santé (`POST /members/addPieceJointe`) | **P0-J** — contrat indécouvrable, l’endpoint ne nomme aucun champ |
| 1 ter | Déposer réellement une déclaration de sinistre (`POST /claims`) | **P0-K** — référentiel `produit` non exposé |
| 1 quater | Tarifer un renouvellement Santé sur l’API | **P1-H** — `409` opaque sur tout payload |
| 1 bis | Redimensionner les photos avant envoi (et non seulement les compresser) | nouvelle dépendance native ; inutile si **P0-I** est traité |
| 2 | Joindre les justificatifs à une demande de résiliation | **P3.17** (base64 au lieu de multipart) |
| 3 | Rétablir le téléchargement d’un document utile | **P0-H** (pas d’URL) |
| 4 | Concevoir l’écran de liste des notifications | décision produit — le compteur est déjà réel |
| 5 | Déplacer le chat hors du sinistre, ou le paramétrer | **P3.12** (arbitrage) |
| 6 | Afficher les relations « frère/sœur » et « autre » d’un bénéficiaire | **P1-G** (enum sans valeur possible) |
| 7 | Retirer la relecture de contrôle après chaque souscription Voyage | **P1-A** |
| 8 | Persister la wilaya du profil santé | **P0-F** (aucun champ dans le contrat) |

### 3.1 Hors périmètre à confirmer

Les quatre points ont été sondés en live le 16/09. Un seul était branchable ; il
l’est désormais.

- [x] ~~**Validation d’adhérent**~~ — ✅ **branché**. L’assistant « Compléter profil
      santé » vidait son brouillon et affichait un écran de succès **sans rien envoyer** :
      cinq étapes de saisie n’atteignaient personne. Il enregistre maintenant le RIB
      (`PUT /members`) et dépose la déclaration (`POST /members/validation`).
      `GET /members/documents` renvoie `[]` — rien à afficher tant que le dépôt de pièces
      est bloqué.
- [ ] ⛔ **Renouvellements et avenants Santé** — **bloqués, pas « non engagés »**. Les deux
      parcours ont une interface complète et rien à appeler : le devis de renouvellement
      répond `409` opaque à tout payload, et le devis d’adjonction **plante sur son propre
      DTO d’erreur** (`Cannot deserialize "409 :" into int`), masquant le refus réel
      derrière un `500`. Voir **P1-H** et **P1-I**.
- [ ] ⬜ **Annulation d’un remboursement** — aucun écran, et un contrat surprenant :
      l’endpoint réclame l’intégralité du formulaire de remboursement à nouveau.
      Développement de fonctionnalité, pas du câblage.
- [ ] ⬜ **Report d’un voyage** — aucun écran. Contrat simple (`dateDebut`,
      `modifyDevis`). Développement de fonctionnalité, pas du câblage.
- [x] ~~**Référentiel des professions**~~ — ✅ branché le 16/09 : les formulaires
      voyageur proposent désormais un sélecteur avec recherche alimenté par
      `GET /professions` (354 entrées), au lieu d’un champ libre dont la valeur partait
      telle quelle vers `POST /personnes/assure`.

---

<a id="t4"></a>

## 4. Registre exhaustif des anomalies

[↑ Sommaire](#haut)

Les lignes marquées 🆕 ont été découvertes lors de la campagne de tests en écriture du
16/09/2026 — aucune n’était visible depuis la documentation.

| Endpoint | Anomalie observée | Attendu | Sévérité | Ouvert depuis |
|---|---|---|---|---|
| 🆕 `POST /beneficiaries` | **Aucun format de date n’est accepté** : le désérialiseur refuse `JJ/MM/AAAA`, le validateur refuse toutes les formes ISO (5 formats testés) | Accorder les deux couches et documenter | 🔴 Bloquant | 16/09/2026 |
| 🆕 `PUT /members` | Remplacement complet et non partiel ; bloc `questionnaire` mal nommé dans le spec ; réponses en chaînes « Oui »/« Non » et non en booléens ; validation non documentée | Documenter le contrat réel | 🔴 Bloquant | 16/09/2026 |
| 🆕 `POST /refunds` | Le champ de l’acte est passé de `type` à `codeActe` **sans préavis** ; l’ancien nom répond `404 « Acte non trouvé »` | Annoncer tout renommage ; déclarer les champs | 🔴 Bloquant | 16/09/2026 |
| 🆕 `GET /documents` | `fichier` est un chemin serveur ; `/assets?path=` répond `404` | Une route de téléchargement, et un `id` par document | 🔴 Bloquant | 15/09/2026 |
| 🆕 `GET /chat/history`, `POST /chat/send` | Aucun paramètre de sinistre : un seul fil par client, affiché sous tous les dossiers | Un paramètre de dossier, ou un arbitrage produit | 🔴 Bloquant | 16/09/2026 |
| `/points-verts/paiement/{quittanceId}` | `500` systématique | `404` / `400` typés | 🔴 Bloquant | 20/07/2026 |
| `/members/bulletinAdhesion` | `500` et absence de réponse au-delà de 12 s | `404` ou le PDF | 🔴 Bloquant | 25/06/2026 |
| `confirmPayment/livraison/{quittanceId}` | Endpoint annoncé, inexistant | L’exposer | 🔴 Bloquant | 30/07/2026 |
| 🆕 `POST /avenant/resiliation` | `justificatif` en base64 là où tout le reste est multipart ; `motifId` sans énumération ; `500` sur contrat inconnu, avec fuite du chemin interne `/scolaire-espace-client/…` | Variante multipart, liste des motifs, `404` | 🟠 Majeur | 16/09/2026 |
| 🆕 `PUT /beneficiaries/updateTaux` | Règle des 100 % non documentée ; le compte de test ne la respecte pas (10 + 19) | Documenter, et seeder une répartition cohérente | 🟠 Majeur | 16/09/2026 |
| 🆕 `LienParenteEnum` / `SexeEnum` | Énumérations fermées non publiées ; « frère/sœur » et « autre » n’ont aucune valeur possible | Publier les valeurs, et couvrir les relations du produit | 🟠 Majeur | 16/09/2026 |
| 🆕 `DELETE /ayantDroits/{id}/demandeSuppression` | Corps multipart déclaré **sans aucun champ** ; `500` sur identifiant inconnu ; aucun état de demande exposé | Documenter les champs, `404`, indicateur d’état | 🟠 Majeur | 15/09/2026 |
| 🆕 `POST /claims` | Jamais appelable : `produit` doit correspondre à un référentiel « produit sinistre » non exposé (10 valeurs testées → `produit_sinistre_id on null`). Le validateur nomme par ailleurs `date_naiss` / `path_files` là où l'endpoint lit `dateNaiss` / `pieces` | Exposer le référentiel ; aligner les noms de champs | 🔴 Bloquant | 16/09/2026 |
| 🆕 `POST /avenants/{id}/renouvellements/sante/devis` | `409 « problem devis Renouvellement Sante »` sur tout payload, sans champ ni raison | Un message exploitable, ou les préconditions | 🟠 Majeur | 16/09/2026 |
| 🆕 `POST /members/addPieceJointe` | Corps multipart déclaré **sans aucun champ** ; toute requête répond `422 « The given data was invalid. »` **sans nommer de champ** ; 25+ noms de parties testés sans succès. Seule propriété identifiable : `rib` (String), via une fuite d’erreur de conversion | Publier la liste des parties, ou nommer les champs manquants | 🔴 Bloquant | 16/09/2026 |
| 🆕 **Tous les téléversements** | Plafond d’environ **1 Mio par fichier** ; dépassement → `500 « Maximum upload size exceeded »` sans nom de champ. Une photo de téléphone (3–5 Mo) ne passe pas | `max-file-size` réaliste ; `413` ou `422` nommant la pièce, avec la limite | 🔴 Bloquant | 16/09/2026 |
| 🆕 `POST /nousContacter` | `500` sur champ manquant (« Required part 'nom' is not present ») | `400` | 🟡 Moyen | 16/09/2026 |
| 🆕 `POST /carteFlash/checkTicketValidation` | `statut` vaut la chaîne `"false"` — booléen déguisé, pas l’énumération que le nom suggère | Un booléen, ou une énumération documentée | 🟡 Moyen | 16/09/2026 |
| `/auth/password-policy` | Endpoint annoncé, inexistant | L’exposer | 🟠 Majeur | 12/08/2026 |
| `/notifications/unread-count` | `404` | `{ count: integer }` | 🟠 Majeur | 25/06/2026 |
| `/refunds/{refundId}/documents` | Régression `200` → `404` | Rétablir ou acter le retrait | 🟠 Majeur | 03/08/2026 |
| `/actes/groups` | Ni catégorie, ni illustration ; `codeActe` non unique | Ajouter les champs, unicité du code | 🟡 Moyen | 28/07/2026 |
| `/promoCode/validate` | Aucun code valide en base | Seeder un code | 🟡 Moyen | 03/08/2026 |
| `/assets` | `500` puis `404` sans `path` | `400`, `path` requis | 🟡 Moyen | 03/08/2026 |
| `/contrats/{contratId}/documents` | `404` avec un identifiant réel | Servir le document | 🟡 Moyen | 25/06/2026 |
| `/a-propos-amana/sos`, `/documents`, `/actes/groups` | Contenu en français quel que soit `accept-language` | Localiser | 🟡 Moyen | 15/09/2026 |
| `/a-propos-amana/sos` | Une ligne de prose (horaires) mélangée à une liste de numéros | Un discriminant de type | ⚪ Mineur | 15/09/2026 |
| `/destination/getGlobal` | `{key, value}` au lieu de `{id, label}` | Aligner sur le DTO | ⚪ Mineur | 25/06/2026 |
| `/beneficiaries`, `/notifications` | Enveloppe de pagination réduite | Une seule forme | ⚪ Mineur | 03/08/2026 |

---

<a id="t5"></a>

## 5. Endpoints intégrés portant une anomalie résiduelle

[↑ Sommaire](#haut)

| Endpoint | Anomalie | Contournement en place | Retrait possible quand |
|---|---|---|---|
| `POST /souscriptions/voyage` | `201 success: true` sur un échec (V8b) | Relecture du contrat après chaque souscription | P1-A corrigé |
| `POST /souscriptions/voyage` | Chaîne de trois appels non transactionnelle (V8c) | Aucun — risque assumé, le contrat restant impayé | P1-B corrigé |
| `POST /simulation-voyage` | Champs obligatoires ignorés, prix faux en `200` (V5) | Appel émis seulement une fois tous les champs réunis | P1-C corrigé |
| `POST /simulation-voyage` | Prime Famille plafonnée au 7ᵉ voyageur (V13) | Aucun — prix serveur affiché tel quel, sur décision produit | P1-D tranché |
| `PUT /members` | Remplacement complet et validation non documentée | Relecture de la fiche et fusion avant chaque écriture ; téléphone complété à 10 chiffres | P0-F documenté |
| `POST /refunds` | Champs non déclarés, nom du champ acte modifié sans préavis | Contrat écrit à la main d’après les essais du 16/09 | P0-G, P2.6 traités |
| Tous les téléversements | Plafond ~1 Mio, refus en `500` anonyme | Photos compressées (qualité 0,6) et fichier mesuré avant envoi, avec un message explicite | **P0-I** traité |
| `PUT /beneficiaries/updateTaux` | Règle des 100 % non documentée | Refus côté application avec un message explicite | P1-F traité |
| `POST /beneficiaries` | Relations « frère/sœur » et « autre » sans valeur possible | Repliées sur « Ascendant », avec un indicateur exposé à l’écran | P1-G traité |
| `GET /chat/history` | Fil par client, écran par sinistre | Aucun — l’identifiant du sinistre ne sert plus qu’au cache | P3.12 tranché |
| `POST /carteFlash/checkTicketValidation` | `statut` en texte libre | Liste blanche : tout statut inconnu est traité comme un refus | P2.8 traité |
| `GET /contrats/{id}` | Personnes assurées, moyen de paiement et valeurs de garantie absents | Champs omis à l’écran | P3.5 traité |
| `GET /auth/me` | DTO plat déclaré, réponse imbriquée | Lecture dans `souscripteur{}` | P2.1 traité |
| `POST /modePaiement/CIB` | `formUrl: null` avec `errorCode: 0` | Les deux champs contrôlés avant redirection | P0-A traité |
| Tous | Cinq formats de dates, deux enveloppes de pagination, quatre enveloppes de réponse | Analyseurs multiples, lecture de page unifiée | P2.3, P2.4, P2.10 traités |

---

<a id="t6"></a>

## 6. Validations de bout en bout jamais exécutées

[↑ Sommaire](#haut)

- [ ] **Paiement par carte — retour de transaction.** Vérifié jusqu’à la redirection
      seulement. **Chemin de test disponible** : Dahabia, sur appareil.
- [ ] **Points Verts — cas nominal.** Seul le cas « solde insuffisant » est vérifié ; un
      test consommerait l’essentiel des 5 000 points du compte.
- [ ] **Retrait d’un ayant droit, résiliation d’un contrat, suppression d’un
      bénéficiaire.** Ces trois opérations sont **destructrices sur le compte de test
      partagé** : seul leur comportement d’erreur a été vérifié le 16/09 (avec des
      identifiants inexistants). **Prérequis** : un compte jetable.
- [ ] **Dépôt des pièces du profil santé.** `POST /members/addPieceJointe` **ne peut pas
      être branché** faute de contrat exploitable (P0-J). La déclaration de remboursement,
      elle, a été vérifiée avec pièces jointes.

> Les autres écritures **ont** été exercées en réel le 16/09 : profil santé, déclaration
> de remboursement, formulaire de contact, carte Flash, chat. Ce que cela a laissé sur le
> compte de test est listé au § 20 de `BACKEND_CHANGES_wire-remaining-app-flows.md`.

---

<a id="t7"></a>

## 7. Ordre de résorption recommandé

[↑ Sommaire](#haut)

### Lot 1 — Débloquer la validation *(backend et prestataire)*

- [ ] **P0-M — revenir dans l'application après le 3-D Secure** *(le paiement marche ;
      il finit sur le site vitrine)*
- [ ] P0-N — `confirmPayment` doit refléter l'encaissement
- [ ] P0-B — `/points-verts/paiement` en erreurs typées
- [ ] P0-C — confirmation du paiement à la livraison
- [ ] P0-D — vérification serveur de l’OTP téléphone
- [ ] P1-D — arbitrage du plafond tarifaire Famille

### Lot 2 — Débloquer les fonctions inopérantes *(backend, découvertes du 16/09)*

- [ ] **P0-I — relever la limite de téléversement** *(le plus urgent : il bloque le cas
      nominal d’une fonction déjà livrée)*
- [ ] P0-J — publier le contrat de `POST /members/addPieceJointe`
- [ ] P0-K — exposer le référentiel `produit` de `POST /claims`, et aligner les noms de
      champs entre le validateur et l’endpoint
- [ ] P0-L — `GET /documents` : servir le catalogue hors adhérents santé
- [ ] P0-H — URL téléchargeable pour les documents utiles
- [ ] P1-F — seeder une répartition à 100 % sur le compte de test
- [ ] P1-G — publier `LienParenteEnum` et `SexeEnum`, et couvrir les relations du produit
- [ ] P3.17 — envoi multipart pour le justificatif de résiliation

### Lot 3 — Arbitrages produit

- [ ] P3.12 — chat : paramètre de sinistre, ou fil unique déplacé vers le menu
- [ ] P3.13 — périmètre de la Santé Groupe
- [ ] Écran de liste des notifications
- [ ] Renouvellements et avenants Santé

### Lot 4 — Fiabiliser le contrat *(backend, sans dépendance externe)*

- [ ] P0-F, P0-G — documenter `PUT /members`, annoncer les renommages
- [ ] P1-A, P1-B, P1-C, P1-E — succès mensonger, atomicité, validation, primes négatives
- [ ] P2.9 — un exemple de requête fonctionnelle par endpoint d’écriture
- [ ] P2.3, P2.10 — format de date unique, enveloppes de réponse harmonisées
- [ ] P2.2, P2.8 — enveloppe d’erreur et énumérations documentées

### Lot 5 — Reliquat applicatif

- [ ] Dépôt des pièces du profil santé — **dès que P0-J est livré**
- [ ] Rejouer les trois opérations destructrices sur un compte jetable

### Lot 6 — Confort et complétude

- [ ] P3.1 à P3.11, P3.14 à P3.16, P3.18, P3.19
- [ ] P2.1, P2.4, P2.5, P2.6, P2.7

---

<a id="t8"></a>

## 8. Annexe — reproduction et outillage

[↑ Sommaire](#haut)

### 8.1 Reproduire les découvertes du 16/09

```bash
BASE="https://gateway.amana.dz/MOBILE/api/v1"
JWT=$(curl -s -X POST "$BASE/auth/login" -H 'Content-Type: application/json' \
  -d '{"username":"test-chahinaz@amana.dz","password":"<mot de passe>"}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["jwt"])')

# P0-F — une mise à jour partielle de /members est refusée
curl -s -X PUT "$BASE/members" -H "Authorization: Bearer $JWT" \
  -H 'Content-Type: application/json' -d '{"sexe":"Féminin"}'
# → 422, listant ville, codePostal, dateEntreeService et les 5 questions

# P0-G — l'ancien nom du champ acte ne fonctionne plus
curl -s -X POST "$BASE/refunds" -H "Authorization: Bearer $JWT" \
  -F "type=Consultation" -F "ayantDroitId=75395" -F "dateSoins=01/09/2026"
# → 404 "Acte non trouvé" ; avec codeActe= le même appel aboutit

# P2.13 — le seul format de date accepté à la création d'un bénéficiaire
curl -s -X POST "$BASE/beneficiaries" -H "Authorization: Bearer $JWT" \
  -H 'Content-Type: application/json' \
  -d '[{"nom":"x","prenom":"y","dateNaissance":"19900101","sexe":"Masculin",
        "lienParente":"Conjoint","adresse":"a","telephone":"0550123252",
        "email":"t@t.dz","taux":100}]'
# → 201 ; « 01/01/1990 » et toutes les formes ISO répondent 400 ou 422

# P3.12 — l'historique du chat ne prend aucun paramètre de sinistre
curl -s "$BASE/chat/history?pageSize=3" -H "Authorization: Bearer $JWT"
# → un identifiant de conversation unique pour tous les messages
```

### 8.2 Outillage de surveillance du contrat

```bash
yarn api:probe      # balaie les 54 endpoints de lecture, n’émet aucune écriture
yarn api:contract   # compare le relevé au contrat figé et signale les régressions
```

Le contrôle distingue une rupture sur un endpoint **réellement consommé** — qui impose
une modification applicative — d’une dérive sur un endpoint non branché. Il a permis, le
27/08/2026, de qualifier en trois minutes un incident de pré-production affectant
22 endpoints.

> **Limite connue de cet outillage** : il ne couvre que les lectures. Les quatre
> anomalies bloquantes du 16/09 portent toutes sur des écritures, qu’aucun balayage
> automatique ne peut exercer sans créer de la donnée. Elles n’ont été trouvées qu’en
> appelant les endpoints à la main. **Un jeu de données jetable en pré-production
> permettrait d’automatiser cette couverture** — c’est la demande la plus structurante
> de ce document.

---

*Document confidentiel — OCTERA pour Amana Assurances · 16 septembre 2026*
