Skip to main content

RECOMMENDATION_IMPROVE_DOC — Plan de refonte doc Fluximmo

Document destiné à devenir /RECOMMENDATION_IMPROVE_DOC.md à la racine du repo une fois validé. Audience : Tim (CEO/Tech Fluximmo) + équipe doc/produit. Objectif : refonte UX/DX de la doc Mintlify doc.fluximmo.io. ⚠️ Scope de cette session : production UNIQUEMENT du fichier RECOMMENDATION_IMPROVE_DOC.md à la racine. Aucune modification des .mdx ni de mint.json n’est entreprise dans cette session — la refonte effective sera planifiée en sprints séparés (cf §11).

1. Contexte

Fluximmo est un acteur historique (depuis 2017) de l’API d’intelligence immobilière FR. La doc actuelle souffre de friction d’onboarding (3 mentions Calendly), d’absence de quickstart/playbooks/FAQ, de nombreux fichiers .mdx orphelins, et de pages API référence quasi-vides (1 ligne openapi: ref). Les concepts critiques (Property vs Advert, cycle de vie alerte, match types) sont sous-documentés ou éparpillés. Objectifs primaires de la refonte :
  1. Autonomie totale : un client doit pouvoir intégrer l’API de bout en bout sans appel ni email, juste avec la doc + sample no-auth + clé test gratuite. Métrique : ratio “tickets posés vs comptes créés”.
  2. Compatibilité LLM : en 2026 la majorité des clients passent par ChatGPT/Claude pour générer leurs payloads. Vu la complexité (filtres nested, geo, match types), les LLMs échouent souvent → les clients accusent l’API. Nouvelle exigence : page llms-context.mdx synthétique copiable/collable + activation du standard llms.txt/llms-full.txt Mintlify.
  3. Supériorité perçue : design premium face à Casafari/Melo, identity visuelle modernisée (couleurs, hero, illustrations, diagrammes).
  4. DX par l’exemple : privilégier exemples concrets > texte brut. Pas d’usine à gaz.

2. État des lieux (synthèse)

3. Décisions confirmées (Q&A)

4. User journeys cibles

Avant de penser endpoints, on pense parcours utilisateur. 8 journeys prioritaires :
  1. Découverte sans clé → samples no-auth + visualisation Property/Advert/webhook payload
  2. Onboarding 5 min → clé test gratuite my.fluximmo.io → premier appel curl → première recherche
  3. Alerte ADVERT temps réel → créer alerte achat appartement département → recevoir webhook → traiter events (prix, dépub/repub)
  4. Recherche analytique PROPERTIES → filtrer Paris achat appartement avec meta.isTotallyOffline=false + range prix m² + paginer par cursor
  5. ★ Recherche géographique avancée → filtrer par geoBoundingBox (carte), geoDistance (rayon autour d’un point), polygone GeoJSON (zone dessinée), polygones administratifs (IRIS, canton, commune…)
  6. Réplication BDD adverts → alerte ADVERT large + persistance locale + sync continue via events
  7. Estimation d’un bien → AI lite estimation → exploitation des price ranges
  8. Géocodage → résoudre une adresse FR → utiliser l’output dans un filtre location search
Chaque journey aura son playbook dédié (cf §7).

5. Décision matrix — ADVERT vs PROPERTY

⚠️ Cycle de vie alerte (concept critique non documenté aujourd’hui) : les events d’une alerte ne couvrent QUE les adverts historiquement matchées par cette même alerte. Supprimer + recréer une alerte = perte d’historique (l’alerte recréée ne recevra plus les events des annonces matchées par l’ancienne). À documenter en gras.
⚠️ Pas de backfill à la création (vérifié dans les workers) : une alerte ne match QUE les adverts/properties traitées après sa création. Les annonces antérieures qualifiantes ne seront jamais émises. → Toujours expliquer comment combiner search (one-shot pour le rétro) + alert (continuous).
⚠️ Events ADVERT exhaustifs réels (workers) : PRICE (toute variation, même 1 €), REPUBLISHED (status passe à online après offline), UNPUBLISHED (status passe à offline). CHECK (vérification sans changement) ne déclenche pas de webhook.

6. Navigation Mintlify proposée

Onglets Mintlify (tabs) à conserver pour navigation rapide par domaine, mais avec la sidebar au-dessus comme source de vérité.

7. Recommandations par endpoint (Search & Alerts)

Pour chaque endpoint : but business · ce qu’il faut documenter · exemples de payloads à créer (filtres, pas JSON) · priorité.

7.1 POST /v2/protected/properties/search

  • But : recherche analytique avec déduplication (1 ligne = 1 bien physique).
  • À documenter : tous les filtres (location avec ses 4+ modes géographiques — cf §7.27 ★, offer, price, habitation, land, parking, type, meta.isTotallyOffline, meta.firstSeenAt/lastSeenAt, tags, process, hasAnomaly, isUrgent, nested adverts.*) ; pagination cursor (searchAfterHash) ; sortBy (5 valeurs) + orderBy ; count semantics ; usage typique vs lite.
  • Exemples prod-driven (cf Annexe D) — ordre par fréquence d’usage réel :
    1. ★★ Combo standard dominant : Paris (postalCode ou inseeCode, pas city qui est ignoré) achat appartement, types [FLAT, HOUSE, PROGRAM], prix 100-350k€, surface 30-110m², 1-3 chambres, isTotallyOffline=false, sort FIRST_SEEN_AT DESC, size 100
    2. ★ Avec geoDistance rayon 20 km autour d’une adresse géocodée (20% prod, cas investisseur)
    3. Variante achat avec range prix m² (ex: Paris 8-15k€/m²)
    4. Bouches-du-Rhône location maison surface ≥ 80 m² 3+ chambres (cas exhaustivité non-prod)
    5. ★ Investisseur terrains : combo standard + filtre land
    6. geoBoundingBox carte interactive (feature distinctive sous-utilisée à promouvoir)
    7. geoShape POLYGON quartier custom (feature distinctive)
    8. Multi-zones via location: [...] (Paris + 92/93/94)
    9. Pagination cursor searchAfterHash sur 5 000 résultats
  • Priorité : P0

7.2 POST /v2/protected/properties/search/lite

  • But : variante allégée, perf plus rapide, schéma de réponse réduit.
  • À documenter : différences vs full (filtres absents : adverts nested, parking, process, tags, hasAnomaly, isUrgent) ; quand utiliser (UI temps réel, mobile, autocomplete).
  • Exemples : 1 même cas que full pour comparer · 1 cas mobile/autocomplete.
  • Priorité : P1

7.3 POST /v2/protected/properties/search/lite/similar

  • But : trouver des biens similaires (à clarifier : similar à un flxId ? à un payload ?).
  • À documenter : input attendu, distance/scoring, cas d’usage (recommandation, AVM peer comparison).
  • Exemples : 1 cas peer comparison.
  • Priorité : P3 (sur demande / capability)

7.4 POST /v2/protected/adverts/search

  • But : recherche brute annonces, avec filtres source-spécifiques.
  • À documenter : filtres communs + spécifiques adverts (isOnline, isPro, isExclusive, isAuction, source, seller[], firstSeenAt, lastModifiedAt, propertyFlxId, mainPropertyAdFlxId, scope, currency) ; même location riche (geoBoundingBox/geoDistance/polygon) — cf §7.27 ★ ; pagination + sorting ; quand préférer à properties search.
  • Exemples :
    1. Achat appartement département (idem playbook chasseur)
    2. geoDistance rayon autour d’une station de métro (chasseur urbain)
    3. Adverts d’un seller spécifique (agence)
    4. Adverts d’une property donnée (propertyFlxId)
    5. Adverts pro vs particulier (isPro)
  • Priorité : P0

7.5 PUT /v2/protected/adverts/search/alerts (CREATE)

  • But : créer une alerte ADVERT → réception webhook temps réel.
  • À documenter : body (search_query = même schéma que search · webhook_url · match enum · search_name · context) ; comportement à la création (back-fill ? non, uniquement nouveaux matches) ; ⚠️ les events ne s’appliquent qu’aux adverts historiquement matchées ; activate/deactivate vs delete (perte historique).
  • Exemples :
    1. Achat appartement département match=ALERT_MATCH_CREATED (use case #1)
    2. Achat + events match=ALERT_MATCH_ADVERT_EVENT
    3. Multi-départements via array
    4. Range prix + surface + chambres
  • Priorité : P0

7.6 PUT /v2/protected/adverts/search/alerts/many (BULK CREATE)

  • But : créer N alertes en un appel.
  • À documenter : limite max, atomicité, cas d’usage (multi-territoires).
  • Exemples : 1 cas multi-départements.
  • Priorité : P2

7.7 GET /v2/protected/adverts/search/alerts/all (LIST)

  • À documenter : pas de pagination visible, format réponse.
  • Priorité : P1

7.8 GET /v2/protected/adverts/search/alerts/{alertId} (READ)

  • À documenter : utilité (réinspecter config), réponse complète.
  • Priorité : P2

7.9 PATCH /v2/protected/adverts/search/alerts/{alertId} (UPDATE)

  • À documenter : ⚠️ modifier l’alerte conserve l’historique (≠ delete + recreate), champs modifiables.
  • Exemples : 1 changement webhook url · 1 ajout filtre prix.
  • Priorité : P1

7.10 DELETE /v2/protected/adverts/search/alerts/{alertId} (DEACTIVATE)

  • À documenter : sémantique réelle (deactivate vs hard delete) ; impact sur historique events.
  • Priorité : P1

7.11 GET /v2/protected/adverts/search/alerts/{alertId}/activate (REACTIVATE)

  • À documenter : pourquoi GET (legacy), comportement post-réactivation (events repris ?).
  • Priorité : P2

7.12 GET /v2/protected/adverts/search/alerts/history/{flxId} (HISTORY)

  • À documenter : que retourne ce endpoint ? historique des matches d’une alerte ; pagination offset (skip/take, ⚠️ incohérence vs cursor du search) ; cas d’usage (rejouer un backlog).
  • Exemples : 1 fetch backlog 30 derniers jours.
  • Priorité : P1

7.13–7.20 Properties alerts (CREATE, MANY, LIST, GET, PATCH, DELETE, ACTIVATE, HISTORY)

  • Structure miroir des adverts alerts. Même grille de doc.
  • Différence clé à mettre en gras : webhook = IDs only · pas d’events sur changement de prix · match enum = CREATED | MERGED | UNKNOWN (pas ADVERT_EVENT). Combo prod par défaut : match=[CREATED, MERGED].
  • Exemples spécifiques (cf Annexe D — patterns prod) :
    1. ★★ Combo standard 88% prod : alerte ville+CP achat [FLAT, HOUSE, PROGRAM] prix 100-350k€ isTotallyOffline=false match=[CREATED, MERGED]
    2. Properties alert sur département + meta.isTotallyOffline=false + refetch IDs reçus
    3. Use case fintech : alerte properties analytics (Paris achat) → fetch périodique pour stats
  • Priorité : P0 (CREATE + LIST), P1 (UPDATE, DELETE, HISTORY), P2 (autres)

7.21 GET /v2/protected/adverts/{flxId} & /v2/protected/adverts/by-url

  • But : refetch unitaire d’une advert.
  • À documenter : codes 404 (advert supprimée du portail), différence by-id vs by-url.
  • Priorité : P1

7.22 GET /v2/protected/properties/{flxId} & /by-url & /bulk & /by-advert

  • But : refetch après webhook properties (IDs only).
  • À documenter : bulk = batch fetch (taille max, perf) ; quand utiliser by-advert ; lien direct webhook → refetch.
  • Exemples : 1 batch fetch 50 IDs après webhook · 1 cas by-advert.
  • Priorité : P0 (bulk + by-id), P1 (autres)

7.23 Sample endpoints (GET /v2/sample/properties|adverts|webhook/properties|webhook/adverts)

  • But : visualisation publique sans auth.
  • À documenter : ★ mettre en avant fortement dans Démarrer ; usage : tester structure avant d’avoir une clé, debug local, fixtures de tests.
  • Priorité : P0

7.24 AI : POST /v2/protected/estimate/property/lite & /v2/protected/experimental/estimate

  • À documenter : input (location, habitation, type, offer), output (price, priceRange, confidence/metadata), différence lite vs experimental, cas d’usage AVM.
  • Exemples : 1 estimation appartement Paris · 1 estimation maison province.
  • Priorité : P1

7.25 GEO : /search, /reverse, /search/csv, /reverse/csv, async projects

  • Nature : ⚠️ C’est le BAN / BANO du gouvernement français (api-adresse.data.gouv.fr) que Fluximmo déploie/proxy — pas une brique propriétaire. À assumer franchement dans la doc : “service public BAN/BANO opéré pour vous, sans rate limit gouv”. Évite de survendre.
  • À documenter : origine BAN/BANO (transparence + crédibilité) ; usage typique (résoudre adresse → injecter dans location search Fluximmo) ; index (address, parcel, poi) ; format GeoJSON ; batch CSV sync vs async ; lien externe vers la spec officielle BAN.
  • Exemples : 1 search “Tour Eiffel” · 1 reverse via lat/lon · 1 batch CSV 10k adresses.
  • Priorité : P1

7.26 Capabilities sur demande (DPE/DVF, similar, market analytics, properties analytics, agences, consumption)

  • Page descriptive unique : présenter la capability + valeur métier + CTA “Activer” (email contact).
  • Pas d’exposition OpenAPI.
  • Priorité : P2

7.26.A ★ Page comparative “DVF vs Fluximmo”

  • Pourquoi : DVF (gouv) est l’alternative gratuite la plus citée par les startups qui hésitent. Une page comparative honnête désamorce la question avant qu’elle ne devienne un blocage commercial.
  • Contenu cible :
    • DVF — forces : prix de vente réels (pas de prix annonce), géolocalisation parcellaire précise, gratuit, donnée notariale fiable.
    • DVF — limites : aucun temps réel (publication trimestrielle avec lag de 6+ mois), pas de caractéristiques détaillées du bien (pas de photos, pas de description, pas de filtres surface/chambres/équipements précis), pas de webhooks, pas de cycle de vie de l’annonce.
    • Fluximmo — forces : temps réel (webhooks adverts/properties), caractéristiques riches (habitation, climat, équipements, photos, full-text), événements (PRICE/REPUBLISHED/UNPUBLISHED), filtres avancés et géo riche, multi-portails dédupliqués.
    • Fluximmo — limites : prix = prix annonce (pas prix de vente notarial), couverture marketplace (ce qui est publié uniquement).
    • Quand choisir lequel : matrice “Si tu veux X → utilise Y”.
    • Combinaison gagnante : Fluximmo (temps réel + caractéristiques) + DVF (prix de vente notariaux historiques) = stack complet. → DVF est dès aujourd’hui disponible chez Fluximmo en capability sur-demande : un seul fournisseur, un seul contrat, pas de double intégration. (Endpoints OpenAPI publics arriveront ensuite.)
    • CTA : “Activer DVF sur votre clé” → email contact.
  • Format : tableau comparatif côte-à-côte + diagramme Mermaid de complémentarité + 1-2 cas d’usage concrets (“AVM” / “compliance scoring”).
  • Path : concepts/dvf-vs-fluximmo.mdx
  • Priorité : P1

7.27.A ★★ Page LLM-ready (llms-context.mdx + llms.txt / llms-full.txt)

  • Pourquoi : objectif primaire #2. Les clients passent par ChatGPT/Claude. Les payloads complexes (FilterLocation imbriqué, filtres nested, match types) font échouer les LLMs → accusations injustes. Solution : fournir un bloc de contexte compact que le client copie-colle dans son LLM avant de demander un payload.
  • 2 livrables complémentaires :
    1. /llms-context.mdx (page humaine + lien “Copier pour LLM”) : version condensée et lisible, accessible depuis la sidebar Démarrer + bouton flottant “Copier pour ChatGPT/Claude” (composant Mintlify clipboard).
    2. /llms.txt + /llms-full.txt (servis à la racine, standard llms.txt natif Mintlify) : versions plain-text optimisées pour l’ingestion automatique des LLMs (un index court + une version full).
  • Contenu cible (synthétique mais suffisant) :
    1. Identité Fluximmo en 3 lignes + URL doc + URL sample no-auth (pour que le LLM puisse vérifier).
    2. Décision matrix Property vs Advert ultra-condensée (5 lignes max).
    3. Schémas TypeScript-like des principaux DTOs : FilterProperty, FilterAdvert, FilterLocation (4 modes géo + 4 champs administratifs), FilterPrice, FilterTypeHabitation, FilterMeta, SaveSearchPropertiesPayload, SaveSearchAdvertsPayload.
    4. Enums clés littéraux : EnumPropertyOfferType, EnumPropertyClass, EnumSearchRequestSortBy, EnumPropertyAlertMatch, EnumAdvertAlertMatch, EnumPricingScope, EnumCurrency.
    5. Limites strictes (size 25/100, max keywords 20, distance > 0, bbox topLeft.lat > bottomRight.lat).
    6. Match types & events : CREATED / MERGED (= UPDATED côté worker) / ADVERT_EVENT / events PRICE / REPUBLISHED / UNPUBLISHED. Pas de backfill à la création.
    7. 8-10 archetypes de payloads (texte uniquement, format JSON minifié) couvrant : alerte ADVERT département, search PROPERTIES Paris, geoBoundingBox, geoDistance, geoShape POLYGON, multi-zones INSEE, filtres prix m², refetch bulk properties.
    8. Anti-patterns critiques (1 paragraphe) : ne pas mocker, ne pas mixer GeoJSON [lon,lat] avec API {lat,lon}, ne pas reposer sur backfill.
    9. Lien vers la doc complète et les sample endpoints no-auth.
  • Contraintes de format :
    • Cible 2 000–4 000 tokens max (= ~5-10 K caractères) pour tenir dans une fenêtre prompt LLM standard.
    • Pas de fluff marketing. Notation type schémas TS / JSON / Markdown brut.
    • Mintlify : utiliser <RequestExample> ou bloc <CodeGroup> contenant un <copy> button explicite.
  • Maintenance :
    • Générer (semi-)automatiquement à partir des DTOs (api_fluximmo_v2/src/modules/app/dto/) — script Node ou Python qui parse les classes class-validator/decorators et émet le contexte. Sinon mise à jour manuelle bimensuelle.
    • Versionnée (timestamp en bas) pour que les LLMs sachent la fraîcheur.
  • Bonus DX :
    • Endpoint GET /llms.txt et GET /llms-full.txt côté Mintlify déclaré dans mint.json (feature native).
    • Lien direct dans le footer de chaque page : ”🤖 Copier le contexte LLM”.
  • Priorité : P0 (blocant pour l’objectif autonomie #2)

7.26.B ★ Page “Recevoir des webhooks à fort volume — bonnes pratiques”

  • Pourquoi : un client mal préparé qui prend tout en synchrone perd des webhooks (timeout, 5xx, surcharge) et accuse l’API. Mécaniser les bonnes pratiques en amont réduit massivement le support.
  • Contenu cible :
    • Architecture recommandée : endpoint webhook → ack rapide (200 < 1s) → push vers queue → worker consume async.
    • Outils suggérés : RabbitMQ, AWS SQS, Google Pub/Sub, Redis Streams, Kafka (par ordre de complexité). Pour 80% des clients : SQS standard ou RabbitMQ suffit.
    • Règles de base :
      1. Ne jamais traiter en synchrone dans le handler webhook (DB write, fetch externe, ML inference)
      2. Idempotence : utiliser flxId + event_type + timestamp comme clé de dédup
      3. Validation x-webhook-key (cf §C.3) avant ack
      4. Réponse 2xx rapide (Fluximmo retry 2× + DLX 10× × 5min sinon DROP — cf §C.3)
      5. Persister les payloads bruts dans une table webhook_inbox avant ack (audit + replay)
      6. Dimensionner la queue : SLO Fluximmo 30-50s, batch max 20 adverts → pic possible
    • Anti-patterns : retourner 200 sans persister, ack avant validation, traitement bloquant en handler, pas de queue dead-letter côté client.
    • Diagramme Mermaid : flow webhook → queue → workers → DB.
    • Snippets : exemple Node Express + SQS, exemple Python Flask + Celery/RabbitMQ.
  • Path : playbooks/webhook-volume-architecture.mdx (joint à concepts/webhooks.mdx)
  • Priorité : P1

7.27 ★ Filtres géographiques avancés (transverses search + alerts)

  • Pourquoi un focus dédié : argument distinctif Fluximmo très demandé. Aujourd’hui éparpillé/sous-documenté. Doit être un point d’entrée premium dans Concepts + playbook autonome.
  • Vérité code (cf elasticimmo Go) : 4 modes natifs ES + champs administratifs indexés + multi-zones via repeated location (OR avec minimum_should_match=1). Champ index : location (geo_point WGS84). DistanceType = Plane (à signaler pour longues distances).
  • Modes réels à documenter (tous dans FilterLocation[] du payload search/alerts) :
    1. Champs administratifs indexés : inseeCode (commune), irisCode (quartier), postalCode, department. ⚠️ L’INSEE commune fait l’objet d’une lookup vers centroïde (3 686 communes mappées). Pas de support dédié regionCode/cantonCode/arrondissementCode au niveau du DTO API actuel — à valider avec produit si on étend.
    2. geoBoundingBox : topLeft + bottomRight (LatLng). Cas type : carte interactive “search in this map area”.
    3. geoDistance : pin (LatLng) + distanceKm. Cas type : “à X km d’une adresse / station”.
    4. geoInseeCodeDistance : inseeCode (commune) + distanceKm. Lookup centroïde → distance. Cas type : “à 10 km de Bordeaux centre”.
    5. geoShape : typePOINT | LINE_STRING | POLYGON | MULTI_POINT | MULTI_LINE_STRING + coordinates (LatLng[]) + relationWITHIN | CONTAINS | DISJOINT | INTERSECTS. Cas type : zone dessinée à la main.
    6. Multi-zones : location: [zone1, zone2, ...] → OR (matching ≥1 zone). Mix bbox + distance + shape autorisé dans la même requête.
  • Contraintes & anti-patterns (réels code) :
    • distanceKm > 0 strict (rejet InvalidArgument)
    • topLeft.lat > bottomRight.lat (latitudes décroissantes N→S)
    • DistanceType Plane (euclidien) → erreur > ~50 km, à signaler
    • Polygons : ES default ≈ 10 000 vertices max (silencieux)
    • bbox immenses : ES tronque silencieusement
    • Coords WGS84, ordre GeoJSON [lon, lat] au stockage interne (l’API reçoit {lat, lon})
  • À documenter aussi :
    • Combinaison filtres : geoDistance + price + propertyType (cas chasseur typique)
    • Pipeline complet : geocoding BAN/BANO → coords → geoDistance ou geoShape → search Fluximmo
    • Performance : impact pagination cursor, sortBy=RELEVANCE est BM25 + boosts title^2/description^3 (pas distance), recommandation bbox vs polygon
  • Exemples à créer :
    1. geoBoundingBox Paris zoom 14 (carte interactive SeLoger-like)
    2. geoDistance 1.5 km autour adresse (chaîné après BAN)
    3. geoInseeCodeDistance 10 km autour commune (cas province)
    4. geoShape POLYGON quartier custom (Le Marais)
    5. ★ Multi-zones : inseeCode Paris + departement 92/93/94
    6. ★ Multi-polygones (3 secteurs d’une agence)
    7. ★ geoDistance + price m² + isTotallyOffline=false (chasseur typique)
  • Endpoints concernés : properties search (full + lite + similar), adverts search, properties alerts CREATE/UPDATE, adverts alerts CREATE/UPDATE.
  • Livrables doc :
    • Page concept concepts/recherche-geographique.mdx (P0)
    • Playbook playbooks/recherche-geographique-avancee.mdx (P0)
    • Mention systématique avec lien depuis chaque endpoint search/alert
  • Priorité : P0

7.30 ★ Page “Pour les startups & sur-mesure” (positionnement commercial discret)

  • Pourquoi : les startups early-stage hésitent face à un acteur “B2B mature”. Une page dédiée + discount + flexibilité signale l’ouverture, sans surfacer Calendly partout.
  • Contenu cible :
    • Engagement explicite : on accompagne les startups, on est ouverts aux collaborations sur-mesure, modalités flexibles.
    • Discount early-stage mentionné clairement (sans afficher de prix — invite à contacter pour qualif).
    • Solutions sur-mesure : endpoints non publics, formats de données customs, alertes complexes multi-territoires, intégrations dédiées.
    • Crédibilité : “nous alimentons aujourd’hui les principaux acteurs de l’immo en France” (sans noms).
    • Confidentialité clients (point critique) : nous ne communiquons jamais publiquement les noms de nos clients sauf autorisation explicite. Références et études de cas disponibles sur demande dans un cadre commercial confidentiel.
    • Cas d’usage typiques que nous accompagnons : réplication BDD marketplaces, scoring fintech, AVM banques/assurances, alertes chasseurs immo, market intelligence, CRM agences.
    • CTA discret : email contact + lien my.fluximmo.io (clé test gratuite 1 semaine).
  • Path : ressources/startups-et-sur-mesure.mdx (visible dans Ressources, pas dans le path d’onboarding pour ne pas alourdir)
  • Bonus : insérer une bannière discrète (“Vous êtes une startup early-stage ? Discount + collaboration sur-mesure →”) en pied de page de l’introduction et du quickstart.
  • Priorité : P1

7.31 ★ Confidentialité clients & références sur demande (transverse)

  • Règle absolue : aucun nom client public dans toute la doc. Pas de logos clients, pas de testimonials nominatifs, pas de “they trust us”.
  • À la place : tournures factuelles “leaders du marché immobilier français”, “marketplaces, banques, fintechs, scoring”, références disponibles sur demande.
  • À mentionner brièvement dans : introduction.mdx, ressources/startups-et-sur-mesure.mdx, FAQ (“Qui sont vos clients ?”).
  • Priorité : P1 (consigne éditoriale plus que livrable séparé)

7.32 Nettoyage des @TODO existants

7.29 ★ Override des titres OpenAPI dans les pages MDX

Problème actuel : https://doc.fluximmo.io/api-v2-reference/properties/get-a-property-by-its-flx_id affiche un titre auto-généré du swagger collé au nom du tag (PropertiesGet a property by it's flx_id) — illisible. Solution Mintlify confirmée : on peut override directement dans le frontmatter du .mdx, sans toucher au swagger :
Action : passer en revue tous les fichiers OpenAPI MDX et ajouter un frontmatter complet (title FR, sidebarTitle court, description orientée cas d’usage, icon Lucide/Font Awesome cohérent par groupe, tag STABLE / BETA / DEPRECATED). Alternative (si on préfère central) : x-mint: { metadata: { title, sidebarTitle, description } } dans le swagger source. Recommandation : frontmatter MDX pour la flexibilité (la doc équipe édite côté MDX, pas côté swagger qui est auto-généré par NestJS). Priorité : P0 (massive amélioration UX/SEO immédiate, ~2h pour tous les endpoints).

7.28 ★ Refonte UI/UX Mintlify (identité visuelle premium)

Objectif : doc qui transpire la maturité (acteur 2017+) et inspire confiance face à Casafari/Melo. Pas un facelift cosmétique — un signal de sérieux et de modernité.

Capacités Mintlify à exploiter

  • Background décoratif : "background": { "decoration": "gradient", "color": { "light": "#…", "dark": "#…" } } → ambiance premium discrète.
  • Theme-aware colors : light/dark paire par paire pour chaque variable.
  • Custom CSS (Tailwind via composants JSX inline) : hero cards, badges custom.
  • Mode dark/light : ergonomie attendue par les devs.
  • Hero React custom sur la landing (composant <HeroCard> JSX).
  • Mermaid diagrams natifs (workflows webhook, Property↔Adverts).
  • API Playground interactif (intégré OpenAPI Mintlify) : bouton “Try it” sur chaque endpoint avec edition live du payload.
  • <Frame> pour captures d’écran my.fluximmo.io.
  • AI search assistant Mintlify (built-in, à activer) : un autre chemin pour atténuer la friction “je ne trouve pas ma réponse”.
  • Reusable snippets (Mintlify) : pour la décision matrix, le LLM banner, le footer contact — déduplication systématique.

Direction artistique

  • Décision : rebrand complet de la palette (palette actuelle #ED0040 rouge + #7274E4 violet → abandonnée). Le rouge dominant n’est plus aligné avec le positionnement “API d’intelligence immobilière mature B2B”.
  • Direction recommandée à valider en atelier brand :
    • Primary : #1E40AF indigo profond (confiance, FR, données) — ou variante #0F172A slate très foncé pour un look encore plus premium/data-driven
    • Secondary/accent : #10B981 emerald (succès, signal “live data”) OU #F59E0B amber (insight, valorisation) — un seul accent fort, choix à valider
    • Neutres : tons gris/slate Tailwind (#F8FAFC light → #0F172A dark)
    • Background : gradient discret depuis le primary (light: linear-gradient(135deg, #F8FAFC 0%, #EEF2FF 100%) ; dark: linear-gradient(135deg, #0F172A 0%, #1E293B 100%))
  • Livraison palette : 1 atelier brand (1h) → Figma palette → application progressive. Livrer la doc avec un placeholder palette neutre (slate-only) et appliquer la palette finale en P2.
  • Cohérence brand global : prévenir l’équipe que ce rebrand peut/devrait remonter au site marketing (fluximmo.io) pour cohérence — décision au-delà du périmètre doc.
  • Typographie : Mintlify default (Inter) suffit, ne pas surcharger.
  • Illustrations : 3-5 spots SVG sur landing + 1 par section (Démarrer / Concepts / Playbooks / API Reference / Ressources). Style “minimal isometric” ou “abstract gradient” — éviter les stock photos. Source : commande illustrateur freelance (Figma / Storyset payant) ou génération AI cohérente.
  • Diagrammes Mermaid systématiques pour :
    • Flow ingestion → indexation → match → webhook
    • Decision tree Property vs Advert
    • Lifecycle d’une alerte (create → match → event → delete)
    • Pipeline geocoding → search Fluximmo
    • Retry/DLX webhook (avec timeline)
  • Hero landing : phrase d’accroche forte (“L’API d’intelligence immobilière FR, depuis 2017”), 3 cards d’entrée (Découvrir / Démarrer en 5 min / Référence API), illustration de fond gradient.

Détails Mintlify config (mint.json)

  • Activer : "theme", "appearance": { "default": "system" }, "background": { "decoration": "gradient" }, "navigation": { ... } propre.
  • Activer API playground sur tous les endpoints documentés.
  • Activer Mintlify search AI (clé Mintlify Pro requise — à confirmer plan).
  • Activer llms.txt natif ("llms_txt": true ou équivalent — cf §7.27.A).
  • Topbar : conserver “Mon compte” (CTA fort), retirer ou minimiser “Réserver un appel” (cf objectif autonomie).

Priorité

  • P1 pour la palette + background + Mermaid systématique + API playground
  • P2 pour les illustrations sur-mesure + Hero landing custom

8. Composants Mintlify à introduire

9. Tableau 1 — Endpoint × Documentation × Exemples × Priorité

10. Tableau 2 — MDX × Goal × Priorité × Dépendances

11. Roadmap priorisée

P0 — Foundation (livraison sprint 1, ~5-7j)

  • Nettoyer mint.json (orphelins, doublons), nouvelle structure 5 sections
  • ★★ Désactiver/minimiser tous les CTAs Calendly (objectif autonomie #1)
  • Nettoyage des @TODO existants (authentification, baas)
  • Réécrire introduction, authentification, quickstart, sample-data
  • Décision matrix Property vs Advert + page concepts associée
  • Refonte webhooks.mdx + page Match types & cycle de vie alerte
  • Page filtres communs
  • ★ Page concept Recherche géographique (4 modes + multi-zones + INSEE commune)
  • ★★ Page LLM-ready llms-context.mdx + activation llms.txt/llms-full.txt Mintlify
  • Override frontmatter title/sidebarTitle/description sur tous les .mdx OpenAPI (~2h)
  • Enrichir 6 endpoints Search/Alerts P0 (properties search, adverts search, properties alert CREATE, adverts alert CREATE, properties bulk, properties get)
  • 3 playbooks fondateurs : Alerte ADVERT département + Search PROPERTIES analytique + ★ Recherche géographique avancée
  • Modèles Property et Advert réécrits

P1 — Coverage + UI/UX (sprint 2, ~5-7j)

  • Tous les autres endpoints alerts (LIST, UPDATE, DELETE, HISTORY)
  • Adverts get + properties by-url/by-advert
  • AI (estimations) + GEO (search/reverse) avec prose et exemples
  • 5 playbooks : Réplication BDD, Track price changes, Estimer un bien, Géocoder, Webhook architecture haut volume (queue)
  • Page comparative DVF vs Fluximmo
  • Page Startups & sur-mesure + bannière discrète early-stage + consigne éditoriale “pas de noms clients publics”
  • FAQ, Codes erreur, Bonnes pratiques, Limites/rate limits, Webhooks-livraison
  • Refonte UI/UX Mintlify : nouvelle palette validée, background gradient, dark mode, Mermaid diagrammes systématiques (5 diagrammes clés), API playground activé, AI search activé (si plan le permet)

P2 — Polish (sprint 3, ~3-5j)

  • Bulk alerts CREATE many (properties + adverts)
  • Activate endpoints
  • GEO async batch
  • Capabilities sur demande (DPE, DVF, similar, market analytics, agences, consumption)
  • Experimental estimate
  • Changelog, Contact, BaaS allégé
  • Illustrations sur-mesure (5-10 SVG) + Hero landing custom (composant React)
  • Snippets Mintlify réutilisables (décision matrix, footer, banners)
  • Script de génération automatique du llms-context.mdx à partir des DTOs (CI/CD)

P3 — Nice-to-have

  • properties/search/lite/similar (sur demande)
  • Mermaid diagrammes (workflow Property/Advert/Webhook)
  • Dark mode visuels, captures my.fluximmo.io
  • Exemples SDK (Python, Node) si lib officielle livrée

12. Indicateurs de succès (post-livraison)

  • Autonomie : ratio “tickets / nouveaux comptes” divisé par 2 sur 3 mois
  • Time-to-first-call < 5 min (depuis landing)
  • CTA Calendly absent avant la 3ᵉ page profondeur
  • Couverture exemples : tout endpoint P0/P1 a ≥ 2 exemples curated + 1 lien playbook
  • Decision matrix Property vs Advert accessible en ≤ 2 clics
  • FAQ couvre les 15 top questions support actuelles (à collecter auprès du support)
  • LLM accuracy : tester llms-context.mdx en demandant à GPT-5 / Claude de générer 5 payloads variés → ≥ 4/5 valides au premier essai
  • UI : score Lighthouse Performance ≥ 90, Accessibility ≥ 95, dark mode fluide

15. Questions ouvertes / décisions à prendre

13. Vérification end-to-end

Une fois la refonte livrée :
  1. Lancer mintlify dev localement → vérifier 0 lien cassé, navigation cohérente
  2. Tester journey “découverte” : depuis introduction, atteindre un sample no-auth en ≤ 2 clics
  3. Tester journey “quickstart” : suivre le quickstart from scratch sur clé test → premier search OK
  4. Lancer un appel curl sur chaque sample endpoint depuis la doc (copier-coller doit marcher)
  5. Vérifier que chaque endpoint P0/P1 a au moins 1 exemple, 1 cas d’erreur, 1 lien croisé concept/playbook
  6. Search interne (Mintlify) : “alerte département” → playbook chasseur en top 3 résultats

Annexe A — Fichiers clés à modifier

Annexe D — Patterns d’usage réels (analyse échantillon récent prod)

Source : /__samples/{adverts_alerts.csv, properties_alerts.csv, requests.csv}.
⚠️ Lecture précautionneuse : ces CSV sont un échantillon récent (requêtes/alertes des dernières semaines), non exhaustifs et non représentatifs en volume global. Les pourcentages cités ci-dessous donnent une tendance qualitative (quels combos existent et lesquels semblent fréquents), pas une distribution prod officielle. À utiliser pour calibrer les archétypes d’exemples, pas pour des assertions chiffrées dans la doc publique.

D.1 Top filtres réellement utilisés

  • Properties (88% des cas) — combo standard : price + offer + location + type + meta + habitation + adverts.isOnline. C’est l’exemple #1 à mettre partout.
  • Properties (8.8%) : combo standard + land (investisseurs terrains).
  • Adverts (54% — bruit) : source-only (probablement tests / monitoring brut). Les clients sérieux passent par Properties.
  • Adverts (12% — sérieux) : type + offer + location + habitation + process.

D.2 Modes géo réels (vs feature distinctive)

⚠️ CRITICAL : le champ city (string) ne filtre PAS réellement (désactivé côté code à cause de la non-normalisation des noms de villes — “Paris” / “PARIS” / “paris 1er” / “Paris 75001”…). Il est silencieusement ignoré. À documenter en gras comme deprecated / ignoré + masquer dans les exemples + question ouverte (cf §15) : faut-il le retirer du DTO ou afficher un warning de validation ?
→ Les clients qui mettent { "city": "Paris", "postalCode": "75001" } filtrent en réalité uniquement par postalCode. Les % ci-dessous reflètent le filtre utile. Décision : la doc met en avant postalCode / inseeCode / department / geoDistance comme modes primaires. city est marqué “non utilisé pour le filtrage” + retiré des exemples. Les modes avancés (geoBoundingBox, geoShape, multi-zones) restent documentés en P0/P1 comme features distinctives.

D.3 Enums dominants

  • offer.type = OFFER_BUY 100% (RENT quasi-nul). → Exemples primaires en achat, location en exemple secondaire pour exhaustivité.
  • type = CLASS_FLAT 85% + CLASS_HOUSE 98% + CLASS_PROGRAM 66% (neuf très utilisé). → Toujours inclure PROGRAM dans les exemples par défaut.
  • meta.isTotallyOffline = false 100%. → Best practice à documenter en gras : c’est le filtre clé pour qualité prod.

D.4 Ranges réels (à utiliser dans les exemples)

  • Prix : médiane 200 k€, p75 358 k€, p95 900 k€. Range typique : 100-350 k€.
  • Surface : médiane 110 m², p95 295 m².
  • Rooms min : médiane 2-3.
  • Rayon geoDistance : médiane 20 km, plage 5-50.

D.5 Pagination réelle

  • size=100 (60% des requêtes), 80 (26%) → utiliser size=100 dans tous les exemples search.
  • sortBy=FIRST_SEEN_AT DESC (62%) → défaut canonique dans la doc.
  • LAST_SEEN_AT DESC (26%) → cas “monitoring activité”.

D.6 Match types réels (alerts properties)

  • Combos fréquents : [CREATED, MERGED] est le défaut prod recommandé.
  • → Documenter ce combo en exemple primaire.

D.7 À déprioriser dans la doc (peu utilisé en prod)

  • keywords / fullTexts (1 cas / 154) — mention brève uniquement.
  • process, tags, hasAnomaly, isUrgent — pas en exemple primaire.
  • Filtre climate.epcEnergy “toutes classes” — non-restrictif, pédagogique seulement.

D.8 Anti-patterns prod observés à signaler

  • ★★ city (string) est silencieusement ignoré — utiliser postalCode, inseeCode, ou department à la place. À mettre en gras dans la doc Concepts/filtres-communs.
  • Adverts source-only : sans valeur métier pour la plupart des cas, signaler comme “test only”.
  • filterProperty vide → renvoie tout le catalogue, danger perf + facturation.
  • Webhook URL non-HTTPS : rejetée à la création (cf §C.3).

D.9 Implication concrète sur la roadmap

  • Les 10 archétypes prod identifiés deviennent les 10 exemples curated obligatoires à livrer en P0/P1 (cf §7 et tableau §9).
  • 80% des clients sont couverts par : combo standard properties + city/postalCode + size=100 + sortBy=FIRST_SEEN_AT DESC + isTotallyOffline=false + match=[CREATED,MERGED] + offer=BUY + types=[FLAT,HOUSE,PROGRAM].

Annexe C — Vérités code à intégrer dans la doc (deltas vs OpenAPI)

Issu de l’exploration des repos api_fluximmo_v2 (NestJS), elasticimmo (Go), prod_fluximmo/v2/_workers (Python).

C.1 Limites & contraintes réelles

C.2 Match types & events (corrigés vs OpenAPI)

  • Adverts :
    • ALERT_MATCH_CREATED : 1× par couple (alert, advert) — dédup garantie via contrainte UNIQUE
    • ALERT_MATCH_ADVERT_EVENT : webhook reçoit event_typePRICE | REPUBLISHED | UNPUBLISHED
  • Properties :
    • ALERT_MATCH_CREATED : nouvelle property
    • ALERT_MATCH_MERGED : property existante mise à jour (côté worker = ALERT_PROPERTIES_MATCH_UPDATED). Le nom “MERGED” est l’API publique, à expliquer pour éviter la confusion.

C.3 Webhook delivery (à documenter en P1)

  • Headers envoyés : Content-Type: application/json, x-webhook-key: <client_token>, x-api-key (legacy duplicate). Pas de signature HMAC, pas de timestamp. Le client doit valider sur la valeur statique de x-webhook-key.
  • Retry : 2 retries internes + 30 s timeout + 10 s sleep entre. Puis DLX : 10 tentatives × 5 min de delay. Total worst case ≈ 50 minutes, ensuite DROP silencieux (pas de notification client).
  • SLO normal : 30–50 s entre match et delivery. À documenter clairement (pas de “temps réel sub-seconde”).
  • Codes succès acceptés : 200–205 (inclus 203, 204, 205 — atypique).

C.4 Codes erreur API (vérité code)

Format réponse erreur :

C.5 Pas de rate limiting exposé

Aucun header X-RateLimit-* retourné par l’API publique. Documenter neutralement : “Si vous rencontrez des throttling, contactez-nous” (sans surpromettre des limites publiques).

C.6 Sample no-auth confirmés

  • GET /v2/sample/properties
  • GET /v2/sample/adverts
  • GET /v2/sample/webhook/properties
  • GET /v2/sample/webhook/adverts
  • GET /portals (no auth, liste portails sources réels)
  • GET /version, GET /healthz (no auth, util DX)

C.7 Schémas DTO clés à cristalliser dans la doc Concepts

  • FilterLocation : 4 modes géo + 4 champs administratifs (cf §7.27)
  • FilterPrice (properties) : initial / latest / highest / lowest + scope[] + currency[] + variation[] + isAuction
  • FilterAdvertPrice : currentPrice.value + currentPrice.valuePerArea
  • FilterMeta (properties only) : firstSeenAt / lastSeenAt / lastUpdatedAt / lastPublishedAt / isTotallyOffline
  • FilterTypeHabitation : surface (total/habitable), roomCount, bedroomCount, bathroomCount, type T1-T5+, characteristics, climate (EPC), heat
  • FilterAdvert-only : isOnline, isPro, isExclusive, isAuction, source, seller[], firstSeenAt, lastModifiedAt, propertyFlxId
  • Enums : EnumPropertyOfferTypeDto (BUY/RENT/AUCTION/LIFE_ANNUITY_SALE/LEASE_BACK/BUSINESS_TAKE_OVER/HOLIDAYS), EnumPropertyClassDto (HOUSE/FLAT/PROGRAM/SHOP/PREMISES/OFFICE/LAND/BUILDING/PARKING/ROOM/OTHER), EnumSearchRequestSortByDto (FIRST_SEEN_AT/PRICE/LAST_UPDATED_AT/LAST_SEEN_AT/RELEVANCE)
  • Scoring RELEVANCE = BM25 + boosts (title^2, description^3, n-gram, fuzziness 1) → à expliquer pour éviter “pourquoi le tri RELEVANCE n’est pas par distance ?”

C.8 Endpoints publics non encore listés

  • GET /portals (no auth)
  • GET /version, GET /healthz (no auth)
  • v1 legacy : GET /v1/adverts/search, GET /v1/adverts/urls, GET /v1/analytics/... → à mentionner brièvement comme legacy (sans encourager)

Annexe B — Concurrents inspirations retenues