Skip to main content

Documentation Index

Fetch the complete documentation index at: https://doc.fluximmo.io/llms.txt

Use this file to discover all available pages before exploring further.

Copiez intégralement le bloc ci-dessous dans votre prompt LLM (ChatGPT, Claude, Cursor) pour qu’il génère des payloads Fluximmo corrects sans hallucination. Cible: 2-4K tokens.
# Fluximmo API v2 — Context (FR real-estate intelligence)

## 1. Identity
Fluximmo is a French real-estate intelligence API (since 2017). Two core entities:
- Property = unique deduplicated physical asset (1 bien = N adverts merged).
- Advert = one listing on one portal (raw, with events).
Full docs: https://doc.fluximmo.io . Sample no-auth: https://api.fluximmo.io/v2/sample/properties

## 2. Property vs Advert (decision matrix)
| Need                                       | Use         |
|--------------------------------------------|-------------|
| Dedup market view, AVM, marketplace        | Property    |
| Per-listing price drops, republish, offline| Advert      |
| Webhook with full payload + events         | Advert      |
| Webhook IDs only (refetch via GET)         | Property    |
| Lightweight autocomplete / mobile          | Lite search |

## 3. Core types (TS-like)
```ts
type SearchPropertiesPayload = {
  size: number,                                  // 1-100 (full) or 1-25 (lite)
  sortBy: EnumSearchRequestSortBy,
  orderBy: "ASC" | "DESC",
  search: { filterProperty: FilterProperty }
}
type SearchAdvertsPayload = {
  size: number,
  sortBy: EnumSearchRequestSortBy,
  orderBy: "ASC" | "DESC",
  search: { filterAd: FilterAdvert }
}
type SaveSearchPropertiesPayload = {
  search_query: { filterProperty: FilterProperty },
  webhook_url: string,                           // HTTPS public URL
  match: EnumPropertyAlertMatch[],               // ["ALERT_MATCH_CREATED"] is the safe default
  search_name: string,
  context?: { metadata?: Record<string, unknown> } // QueryContextDto — optional
}
type SaveSearchAdvertsPayload = {
  search_query: { filterAd: FilterAdvert },
  webhook_url: string,
  match: EnumAdvertAlertMatch[],
  search_name: string,
  context?: { metadata?: Record<string, unknown> }
}

type FilterProperty = {
  location?: FilterLocation[],                   // OR across array entries
  type?: EnumPropertyClass[],
  offer?: { type: EnumPropertyOfferType }[],
  price?: FilterPropertyPrice,                   // {initial|latest}.value.{min,max}
  habitation?: FilterTypeHabitation,
  meta?: FilterMeta
}
type FilterAdvert = {
  location?: FilterLocation,                     // single object (NOT array — unlike FilterProperty)
  type?: EnumPropertyClass[],
  offer?: { type: EnumPropertyOfferType }[],
  currentPrice?: FilterAdvertPrice,              // field name is `currentPrice` (NOT `price`)
  habitation?: FilterTypeHabitation,
  meta?: FilterMeta,
  isOnline?: boolean                             // production: set to true on alerts
}

type FilterLocation = {
  postalCode?: string,                           // "75011"
  inseeCode?: string,                            // "75056"
  department?: string,                           // "75" (2 chars FR)
  irisCode?: string,
  // city is silently IGNORED by the engine — DO NOT USE
  locationCoordinate?: {
    location: {
      geoBoundingBox?: {                         // topLeft.lat > bottomRight.lat AND topLeft.lon < bottomRight.lon
        topLeft: { lat: number, lon: number },
        bottomRight: { lat: number, lon: number }
      },
      geoDistance?: {                            // Plane distance, not Haversine
        pin: { lat: number, lon: number },
        distanceKm: number                       // > 0 required
      }
    }
  }
}
// NOTE: geoShape / POLYGON not exposed. Use geoBoundingBox or multi-zone OR.

type FilterPropertyPrice = {
  initial?: { value: { min?: number, max?: number } },
  latest?:  { value: { min?: number, max?: number } }
}
type FilterAdvertPrice = {
  value?: { min?: number, max?: number },        // EUR
  valuePerArea?: { min?: number, max?: number }  // EUR per m2
}

type FilterTypeHabitation = {
  surface?: { total?: { min?: number, max?: number } },   // m2
  bedroomCount?: { min?: number, max?: number },
  roomCount?:    { min?: number, max?: number },
  floor?:        { min?: number, max?: number }
}

type FilterMeta = {
  isTotallyOffline?: boolean,                       // PROPERTIES production: set to false
  firstSeenAt?:   { min?: string, max?: string },   // ISO 8601 — production typically min: "2025-01-01T00:00:00.000Z"
  lastSeenAt?:    { min?: string, max?: string },
  lastUpdatedAt?: { min?: string, max?: string }
}
```

## 4. Enums (literal values, exact)
```ts
type EnumPropertyOfferType =
  | "OFFER_BUY" | "OFFER_RENT" | "OFFER_LIFE_ANNUITY_SALE"
  | "OFFER_HOLIDAYS" | "OFFER_AUCTION"
  | "OFFER_BUSINESS_TAKE_OVER" | "OFFER_LEASE_BACK" | "OFFER_UNKNOWN"

type EnumPropertyClass =
  | "CLASS_FLAT" | "CLASS_HOUSE" | "CLASS_PROGRAM" | "CLASS_LAND"
  | "CLASS_PARKING" | "CLASS_OFFICE" | "CLASS_SHOP" | "CLASS_PREMISES"
  | "CLASS_BUILDING" | "CLASS_ROOM" | "CLASS_OTHER" | "CLASS_UNKNOWN"

type EnumPropertyAlertMatch =
  | "ALERT_MATCH_CREATED"   // new property matched (safe default)
  | "ALERT_MATCH_MERGED"    // ONLY when a new advert is merged into an already-matched property (advanced, higher volume on large filters)

type EnumAdvertAlertMatch =
  | "ALERT_MATCH_CREATED"      // new advert matched
  | "ALERT_MATCH_ADVERT_EVENT" // PRICE/REPUBLISHED/UNPUBLISHED on previously-matched advert

type EnumSearchRequestSortBy =
  | "FIRST_SEEN_AT" | "LAST_SEEN_AT" | "LAST_UPDATED_AT" | "PRICE" | "RELEVANCE"
type EnumOrderBy = "ASC" | "DESC"

type EnumPricingScope = "PRICING_ONE_OFF" | "PRICING_MENSUAL"
type EnumCurrency     = "CURRENCY_EUR" | "CURRENCY_USD"
```

## 5. Hard limits & auth
- size: 1-100 (full search) or 1-25 (lite search)
- search.keywords / search.fullTexts: max 20 items each
- geoDistance.distanceKm > 0 (else 400)
- geoBoundingBox: topLeft.lat > bottomRight.lat AND topLeft.lon < bottomRight.lon
- API base: https://api.fluximmo.io
- Auth: header `x-api-key: <YOUR_KEY>` (get one at https://my.fluximmo.io, free 1-week trial)

## 6. Match types & events
- PROPERTIES alerts emit:
  - ALERT_MATCH_CREATED: a new property qualified
  - ALERT_MATCH_MERGED: ONLY when a new advert is merged into an already-matched property (not on every check)
- ADVERTS alerts emit (alerts only — no public adverts/search endpoint exists):
  - ALERT_MATCH_CREATED: new advert matched
  - ALERT_MATCH_ADVERT_EVENT: event on a previously matched advert. Event subtypes: PRICE (any change, even 1 EUR), REPUBLISHED (offline -> online), UNPUBLISHED (online -> offline). CHECK is never emitted.
- NO BACKFILL on alert creation: events apply only to items processed AFTER alert creation. PROPERTIES side: combine `search` (one-shot retrospective) + `alert` (continuous). ADVERTS side: ask Fluximmo to replay historical backfill on your webhook by mail to [email protected].
- DELETING + RECREATING an alert loses event history. Use `PATCH /alerts/{flxId}` to modify in place.

## 7. 8 validated archetypes (Phase 3 PASS, 2026-05-04)

### A1. PROPERTIES — Paris achat appartement standard combo
POST /v2/protected/properties/search
```json
{"size":25,"sortBy":"FIRST_SEEN_AT","orderBy":"DESC","search":{"filterProperty":{"location":[{"postalCode":"75001"}],"type":["CLASS_FLAT","CLASS_HOUSE","CLASS_PROGRAM"],"offer":[{"type":"OFFER_BUY"}],"price":{"initial":{"value":{"min":100000,"max":350000}}},"habitation":{"surface":{"total":{"min":30,"max":110}},"bedroomCount":{"min":1,"max":3}},"meta":{"isTotallyOffline":false}}}}
```

### A2. PROPERTIES — Dpt 13 location maison >=80m2 >=3ch
POST /v2/protected/properties/search
```json
{"size":25,"sortBy":"FIRST_SEEN_AT","orderBy":"DESC","search":{"filterProperty":{"location":[{"department":"13"}],"type":["CLASS_HOUSE"],"offer":[{"type":"OFFER_RENT"}],"habitation":{"surface":{"total":{"min":80}},"bedroomCount":{"min":3}},"meta":{"isTotallyOffline":false}}}}
```

### A3. PROPERTIES — geoDistance 20km Tour Eiffel
POST /v2/protected/properties/search
```json
{"size":25,"sortBy":"FIRST_SEEN_AT","orderBy":"DESC","search":{"filterProperty":{"location":[{"locationCoordinate":{"location":{"geoDistance":{"pin":{"lat":48.85837,"lon":2.294481},"distanceKm":20}}}}],"type":["CLASS_FLAT"],"offer":[{"type":"OFFER_BUY"}],"meta":{"isTotallyOffline":false}}}}
```

### A4. PROPERTIES — geoBoundingBox Paris
POST /v2/protected/properties/search
```json
{"size":25,"sortBy":"FIRST_SEEN_AT","orderBy":"DESC","search":{"filterProperty":{"location":[{"locationCoordinate":{"location":{"geoBoundingBox":{"topLeft":{"lat":48.9021,"lon":2.2241},"bottomRight":{"lat":48.8156,"lon":2.4699}}}}}],"offer":[{"type":"OFFER_BUY"}],"meta":{"isTotallyOffline":false}}}}
```

### A5. PROPERTIES — Multi-zones OR (Paris + petite couronne 75/92/93/94)
POST /v2/protected/properties/search
```json
{"size":25,"sortBy":"FIRST_SEEN_AT","orderBy":"DESC","search":{"filterProperty":{"location":[{"department":"75"},{"department":"92"},{"department":"93"},{"department":"94"}],"type":["CLASS_FLAT"],"offer":[{"type":"OFFER_BUY"}],"meta":{"isTotallyOffline":false}}}}
```

### A6. ADVERTS alert — Dpt 75 achat appartement (CREATED only)
PUT /v2/protected/adverts/search/alerts
```json
{"search_query":{"filterAd":{"location":{"department":"75"},"type":["CLASS_FLAT"],"offer":[{"type":"OFFER_BUY"}],"isOnline":true}},"webhook_url":"https://votre-domaine.com/webhooks/fluximmo","match":["ALERT_MATCH_CREATED"],"search_name":"Achat T2-T3 Paris"}
```

### A7. ADVERTS alert — With events (PRICE / REPUBLISHED / UNPUBLISHED)
PUT /v2/protected/adverts/search/alerts
```json
{"search_query":{"filterAd":{"location":{"department":"75"},"type":["CLASS_FLAT"],"offer":[{"type":"OFFER_BUY"}],"currentPrice":{"value":{"max":600000}},"isOnline":true}},"webhook_url":"https://votre-domaine.com/webhooks/fluximmo","match":["ALERT_MATCH_CREATED","ALERT_MATCH_ADVERT_EVENT"],"search_name":"Paris achat - veille prix"}
```

### A8. PROPERTIES alert — Combo standard match=[CREATED]
PUT /v2/protected/properties/search/alerts
```json
{"search_query":{"filterProperty":{"location":[{"postalCode":"75001"}],"type":["CLASS_FLAT"],"offer":[{"type":"OFFER_BUY"}],"meta":{"isTotallyOffline":false}}},"webhook_url":"https://votre-domaine.com/webhooks/fluximmo-properties","match":["ALERT_MATCH_CREATED"],"search_name":"Properties Paris achat appartement"}
```

## 8. Open data — DPE & DVF (4 endpoints)
All POST, base https://api.fluximmo.io, auth header `x-api-key`. Independent from Property/Advert (no `search`/`filterProperty` wrapper). Two access patterns per dataset:
- `*/search` = filtered paginated list, no scoring. `filters` keys are **snake_case**. Cursor pagination.
- `*/match` = ranked candidates by similarity score, for reconciling a listing/asset to open data. `filters` keys are **snake_case**. `scoring_targets` influence ranking only — they never exclude candidates.
DPE = Diagnostic de Performance Énergétique (ADEME). DVF = Demandes de Valeurs Foncières (recorded sales).

### 8.1 Shared filter value shapes
```ts
type NumberRangeFilter = { min?: number, max?: number }   // min=gte, max=lte
type DateRangeFilter   = { from?: string, to?: string }   // ISO date/date-time, from=gte, to=lte
type GeoDistanceFilter = {                                 // all 3 required if used
  lat: number, lon: number,
  distance: string                                         // "<num><unit>", unit: m|km|mi|ft|yd|nmi  e.g. "10km"
}
```

### 8.2 POST /v2/protected/opendata/dpe/search
```ts
type DpeSearchPayload = {
  filters?: DpeInheritFiltersDto,
  sortBy?: "numero_dpe" | "date_etablissement_dpe" | "type_batiment"
         | "surface_habitable_logement" | "etiquette_dpe" | "conso_5_usages_par_m2_ep"
         | "emission_ges_5_usages" | "emission_ges_5_usages_par_m2" | "zone_climatique",
  sortOrder?: "asc" | "desc",
  size?: number,                                           // 1-100, default 20
  cursor?: string
}
type DpeInheritFiltersDto = {
  numero_dpe?: string,
  flx_id?: NumberRangeFilter,
  date_etablissement_dpe?: DateRangeFilter,
  date_visite_diagnostiqueur?: DateRangeFilter,
  date_reception_dpe?: DateRangeFilter,
  date_derniere_modification_dpe?: DateRangeFilter,
  date_fin_validite_dpe?: DateRangeFilter,
  identifiant_ban?: string, numero_voie_ban?: string,
  code_insee_ban?: string, code_postal_ban?: string,
  code_departement_ban?: string, code_region_ban?: string,
  location?: GeoDistanceFilter,
  type_batiment?: ("appartement" | "maison" | "immeuble")[],
  periode_construction?: ("avant 1948" | "1948-1974" | "1975-1977" | "1978-1982"
    | "1983-1988" | "1989-2000" | "2001-2005" | "2006-2012" | "2013-2021" | "après 2021")[],
  surface_habitable_logement?: NumberRangeFilter,
  surface_habitable_immeuble?: NumberRangeFilter,
  surface_ventilee?: NumberRangeFilter,
  nombre_niveau_logement?: NumberRangeFilter,
  nombre_niveau_immeuble?: NumberRangeFilter,
  nombre_appartement?: NumberRangeFilter,
  etiquette_dpe?: ("A"|"B"|"C"|"D"|"E"|"F"|"G")[],         // arrays — OR
  etiquette_ges?: ("A"|"B"|"C"|"D"|"E"|"F"|"G")[],
  conso_5_usages_ep?: NumberRangeFilter,
  conso_5_usages_ef?: NumberRangeFilter,
  conso_5_usages_par_m2_ep?: NumberRangeFilter,
  conso_5_usages_par_m2_ef?: NumberRangeFilter,
  emission_ges_5_usages?: NumberRangeFilter,
  emission_ges_5_usages_par_m2?: NumberRangeFilter,
  qualite_isolation_enveloppe?: ("insuffisante"|"moyenne"|"bonne"|"très bonne")[],
  qualite_isolation_menuiseries?: ("insuffisante"|"moyenne"|"bonne"|"très bonne")[],
  zone_climatique?: ("H1a"|"H1b"|"H1c"|"H2b"|"H2c"|"H2d"|"H3")[]
}
```
Response: `{ data: object[], total: number, size: number, hasMore: boolean, cursor?: string }`

### 8.3 POST /v2/protected/opendata/dpe/match
```ts
type DpeMatchPayload = {
  filters?: DpeFiltersDto,
  size?: number,                                           // 1-20, default 5
  scoring_targets?: MatchDpeScoringTargetsDto
}
type DpeFiltersDto = {
  code_departement_ban?: string, code_insee_ban?: string, code_postal_ban?: string,
  numero_dpe?: string,
  type_batiment?: "appartement" | "maison" | "immeuble",   // single string, NOT array
  surface_habitable_logement?: NumberRangeFilter,          // surface habitable logement m2
  date_etablissement_dpe?: DateRangeFilter,
  location?: GeoDistanceFilter
}
type MatchDpeScoringTargetsDto = {
  conso_5_usages_par_m2_ep?: number,                       // kWh/m2/an (gauss decay)
  emission_ges_5_usages_par_m2?: number,                   // kgCO2/m2/an (gauss decay)
  etiquette_dpe?: "A"|"B"|"C"|"D"|"E"|"F"|"G",
  etiquette_ges?: "A"|"B"|"C"|"D"|"E"|"F"|"G",
  nombre_niveau_immeuble?: number,                         // 0-200
  numero_etage_appartement?: number,                       // -10..200 (negative = basement)
  periode_construction?: string,                           // same enum as periode_construction
  nombre_niveau_logement?: number                          // 0-200
}
```
Response: `{ data: [{ score: number /*0-100*/, confidence: "HIGH"|"MEDIUM"|"LOW"|"REJECT", dpe: {} }], meta: { ambiguous: boolean /*top-2 gap <=5*/, total: number } }`

### 8.4 POST /v2/protected/opendata/dvf/search
```ts
type DvfSearchPayload = {
  filters?: DvfInheritFiltersDto,
  sortBy?: "id" | "transaction_date" | "price_eur"
         | "price_per_m2_living_eur" | "price_per_m2_land_eur" | "surface_m2",
  sortOrder?: "asc" | "desc",
  size?: number,                                           // 1-100, default 20
  cursor?: string
}
type DvfInheritFiltersDto = {
  id?: string,
  transaction_date?: DateRangeFilter,
  transaction_type?: ("Vente" | "Vente en l'etat futur d'achevement"
    | "Vente terrain a batir" | "Echange")[],              // exact strings, unaccented
  price_eur?: NumberRangeFilter,
  price_per_m2_living_eur?: NumberRangeFilter,
  price_per_m2_land_eur?: NumberRangeFilter,
  location?: GeoDistanceFilter,
  departement_code?: string, insee_code?: string,
  postal_code?: string, iris_code?: string,
  iris_type?: ("H"|"A"|"D"|"Z")[],
  property_type?: ("Appartement"|"Maison"|"Autre"|"Terrain"
    |"Tertiaire"|"Dépendance"|"Volume")[],                 // accented, capitalized
  surface_m2?: NumberRangeFilter,
  main_rooms?: NumberRangeFilter,
  land_surface_m2?: NumberRangeFilter,
  is_new_build?: boolean,
  is_open_market?: boolean,
  carrez_surface_m2?: NumberRangeFilter,
  soil_surface_m2?: NumberRangeFilter,
  garden_surface_m2?: NumberRangeFilter
}
```
Response: `{ data: object[], total: number, size: number, hasMore: boolean, cursor?: string }`

### 8.5 POST /v2/protected/opendata/dvf/match
```ts
type DvfMatchPayload = {
  filters?: DvfFiltersDto,
  size?: number,                                           // 1-20, default 5
  scoring_targets?: MatchDvfScoringTargetsDto
}
type DvfFiltersDto = {
  departement_code?: string, insee_code?: string, postal_code?: string,
  id?: string,
  property_type?: "Appartement"|"Maison"|"Autre"|"Terrain"
    |"Tertiaire"|"Dépendance"|"Volume",                    // single string, NOT array
  surface_m2?: NumberRangeFilter,                          // surface habitable m2
  transaction_date?: DateRangeFilter,
  location?: GeoDistanceFilter
}
type MatchDvfScoringTargetsDto = {
  price_eur?: number,                                      // EUR (gauss decay)
  main_rooms?: number,                                     // weighted filter
  is_new_build?: boolean,
  transaction_type?: "Vente" | "Vente en l'etat futur d'achevement"
    | "Vente terrain a batir" | "Echange"
}
```
Response: `{ data: [{ score: number /*0-100*/, confidence: "HIGH"|"MEDIUM"|"LOW"|"REJECT", dvf: {} }], meta: { ambiguous: boolean, total: number } }`

### 8.6 DPE/DVF examples

#### O1. DPE search — appartements F/G dpt 75, surface 30-70 m2
POST /v2/protected/opendata/dpe/search
```json
{"filters":{"code_departement_ban":"75","type_batiment":["appartement"],"etiquette_dpe":["F","G"],"surface_habitable_logement":{"min":30,"max":70}},"sortBy":"date_etablissement_dpe","sortOrder":"desc","size":20}
```

#### O2. DPE search — geo 10km + DPE établi depuis 2024
POST /v2/protected/opendata/dpe/search
```json
{"filters":{"location":{"lat":48.8566,"lon":2.3522,"distance":"10km"},"date_etablissement_dpe":{"from":"2024-01-01T00:00:00.000Z"}},"size":50}
```

#### O3. DPE match — reconcile a listing to its DPE
POST /v2/protected/opendata/dpe/match
```json
{"filters":{"code_postal_ban":"75013","type_batiment":"appartement","surface_habitable_logement":{"min":52,"max":62},"location":{"lat":48.8323,"lon":2.3556,"distance":"300m"}},"size":5,"scoring_targets":{"etiquette_dpe":"D","conso_5_usages_par_m2_ep":210,"numero_etage_appartement":3}}
```

#### O4. DVF search — ventes appartements dpt 13, prix 150k-300k depuis 2023
POST /v2/protected/opendata/dvf/search
```json
{"filters":{"departement_code":"13","property_type":["Appartement"],"transaction_type":["Vente"],"price_eur":{"min":150000,"max":300000},"transaction_date":{"from":"2023-01-01"}},"sortBy":"transaction_date","sortOrder":"desc","size":20}
```

#### O5. DVF match — comparable sales for a 3-room flat
POST /v2/protected/opendata/dvf/match
```json
{"filters":{"insee_code":"83137","property_type":"Appartement","surface_m2":{"min":60,"max":75},"transaction_date":{"from":"2022-01-01"}},"size":5,"scoring_targets":{"price_eur":285000,"main_rooms":3,"transaction_type":"Vente"}}
```

## 9. Anti-patterns (read before generating)
- DO NOT confuse Property and Advert. Property = unique deduplicated asset (webhook = IDs only, refetch needed). Advert = one listing on one portal (webhook = full payload + events).
- DO NOT use `city` in FilterLocation: it is silently IGNORED. Use postalCode / inseeCode / department / irisCode.
- DO NOT use geoShape / POLYGON: not currently exposed. Use geoBoundingBox or geoDistance, optionally combined with multi-zone OR (array of FilterLocation entries).
- DO NOT mix GeoJSON `[lon, lat]` with API `{lat, lon}`: the API everywhere uses `{lat, lon}` objects, never arrays.
- DO NOT swap geoBoundingBox corners: topLeft.lat must be > bottomRight.lat AND topLeft.lon must be < bottomRight.lon.
- DO NOT confuse FilterPropertyPrice (`price.initial.value.{min,max}` / `price.latest.value.{min,max}`) with FilterAdvertPrice (`currentPrice.value.{min,max}` — different field name AND nested under `value`). They are not interchangeable.
- DO NOT rely on backfill at alert creation: alerts only match items processed AFTER creation. Combine search + alert.
- DO NOT delete + recreate an alert to modify it (loses event history). Use `PATCH /alerts/{flxId}`.
- DO NOT use Properties alerts to track per-listing price changes: PRICE / REPUBLISHED / UNPUBLISHED events only exist on Adverts alerts (`ALERT_MATCH_ADVERT_EVENT`).
- DO NOT skip `meta.isTotallyOffline`: production best practice is `meta.isTotallyOffline=false` to exclude completely offline items.
- DO NOT use `ALERT_MATCH_MERGED` casually on Properties alerts: it multiplies webhook volume. Default to `["ALERT_MATCH_CREATED"]`.
- Open-data filters are snake_case on BOTH `*/search` AND `*/match` (`code_departement_ban`, `transaction_date`, `surface_m2`…). Only the pagination params (`sortBy`, `sortOrder`, `cursor`, `size`) stay camelCase.
- DO NOT wrap DPE/DVF payloads in `search`/`filterProperty`: the body is flat (`filters`, `size`, `sortBy`/`scoring_targets`).
- DO NOT pass arrays to `type_batiment` / `property_type` on `*/match`: those are single strings (arrays only exist on `*/search`: `type_batiment`, `property_type`).
- DO NOT use `scoring_targets` to filter: they only re-rank candidates, never exclude. Use `filters` to restrict the candidate set.
- DO NOT accent or recapitalize `transaction_type` values: use exact strings like `Vente en l'etat futur d'achevement` (no accents). `property_type` values ARE accented/capitalized (`Appartement`, `Dépendance`).
- DO NOT omit `distance` on `location`: the open-data geo filter requires `{lat, lon, distance}` all three, with a unit suffix (e.g. `"10km"`).

## 10. Links
- Full docs: https://doc.fluximmo.io
- Sample no-auth Property: https://api.fluximmo.io/v2/sample/properties
- Sample no-auth Advert:   https://api.fluximmo.io/v2/sample/adverts
- Sample webhook Property: https://api.fluximmo.io/v2/sample/webhook/properties
- Sample webhook Advert:   https://api.fluximmo.io/v2/sample/webhook/adverts
- Concepts: /concepts/property-vs-advert , /concepts/match-types-cycle-alerte , /concepts/recherche-geographique , /concepts/webhooks
- Free trial API key: https://my.fluximmo.io (1 week, no commercial calls)

Last updated: 2026-05-16
Token count target: 2000-4000 tokens (~5-10K chars)