---
{
  "title": "Construire un répertoire FHIR de prestataires pour Medicare Plan Finder",
  "description": "CMS exige que les régimes Medicare Advantage publient un répertoire de prestataires FHIR conforme à PDex Plan-Net. Avec l'exportation en masse $export et _typeFilter, l'extraction des bonnes données se fait en un seul appel, et le reste n'est que du code ordinaire.",
  "date": "2026-07-06",
  "author": "Akim Khalitov, Gleb Markin",
  "tags": [
    "Compliance",
    "Integrations",
    "Payerbox"
  ],
  "reading-time": "7 min read",
  "tldr": "CMS exige désormais que chaque régime Medicare Advantage publie un répertoire de prestataires pour Medicare Plan Finder, qui le parcourt quotidiennement sous forme de fichiers publiés, et non d'une API en direct. CMS accepte deux formats; nous utilisons le format FHIR : une exportation en masse $export extrait les ressources du réseau, un traitement en flux ne conserve que les enregistrements référencés, et une publication atomique sert les Bundles FHIR résultants, ainsi qu'un manifeste d'index, via un point d'accès adapté aux robots d'exploration.",
  "utm-campaign": "feature",
  "utm-content": "provider-directory",
  "hide-comments": true
}
---

Si vous gérez un régime Medicare Advantage, CMS exige désormais que vous publiiez un
[répertoire de prestataires](https://www.health-samurai.io/articles/mpf-provider-directory-medicare-advantage-cms-4208-f2)
indiquant quels prestataires et établissements font partie du réseau pour chaque régime, afin que
[Medicare Plan Finder](https://www.medicare.gov/plan-compare/) (MPF) puisse répondre à la question
que posent réellement les assurés : mon prestataire est-il couvert ? Cette question est propre aux
régimes MA : un régime MA couvre un réseau contractuel, et puisque MA est un régime Medicare à
financement fédéral administré par des assureurs privés, CMS a le droit de dicter la façon dont
le répertoire est publié. Le répertoire doit être lisible par machine, mis à jour dans les 30 jours
suivant tout changement, et attesté une fois par an. Il s'inscrit dans
[la démarche générale de CMS en faveur de l'interopérabilité fondée sur FHIR](https://www.health-samurai.io/articles/understanding-the-cms-0057-f-interoperability-and-prior-authorization-final-rule).

CMS accepte deux formats, un fichier JSON plat ou FHIR ; nous construisons le format FHIR. Il suit
[PDex Plan-Net](https://hl7.org/fhir/us/davinci-pdex-plan-net/), le IG Da Vinci qui
standardise le réseau d'un régime à l'aide de profils FHIR sur
[InsurancePlan](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-InsurancePlan.html),
[Organization](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-Organization.html)
(à la fois réseau et établissement),
[Practitioner](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-Practitioner.html),
[PractitionerRole](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-PractitionerRole.html),
[Location](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-Location.html) et
[OrganizationAffiliation](https://hl7.org/fhir/us/davinci-pdex-plan-net/StructureDefinition-plannet-OrganizationAffiliation.html),
ainsi que les références entre eux. CMS n'interroge pas votre serveur pour chaque assuré : son
robot d'exploration ingère vos fichiers publiés une fois par jour et les valide, en suivant son
[guide technique de mise en œuvre](https://www.cms.gov/files/zip/mpf-ma-provider-directory-technical-guide-02182026-final-zip.zip).
Si la validation échoue, si l'attestation annuelle est manquée ou si CMS détecte un dépassement de
son seuil de qualité des données, CMS supprime votre répertoire de MPF, généralement en le
rétablissant le jour suivant la résolution du problème. Pendant la suppression, les assurés qui
comparent les régimes ne peuvent pas voir votre réseau; il est donc essentiel que le répertoire
soit valide à chaque exécution.

Le reste de cet article décrit ce pipeline, étape par étape.

## Fonctionnement

Vos données se trouvent déjà dans Payerbox. Le pipeline les transforme en fichiers que CMS
parcourt, selon un calendrier. Quatre étapes :

```mermaid
flowchart LR
    A["1 · Extraire<br/>$export + _typeFilter<br/>(restreint les parents)"] --> B["2 · Filtrer<br/>résoudre les références,<br/>conserver les enfants"]
    B --> C["3 · Regrouper<br/>FHIR + index.json"]
    C --> D["4 · Publier<br/>stockage infonuagique"]
    D --> E["CMS parcourt<br/>quotidiennement"]
```

*L'exécution quotidienne, de bout en bout : extraction depuis Payerbox, filtrage vers le
sous-ensemble du réseau, regroupement en FHIR, puis publication des fichiers que CMS parcourt.*

Une tâche planifiée exécute l'ensemble chaque matin, avant le parcours quotidien de CMS.

Tout ce qui suit l'extraction n'est que du code ordinaire sur NDJSON, ce qui permet l'exécution
et les tests à partir d'un exemple d'exportation, sans serveur FHIR en direct.

## Extraction des données

Il existe deux façons d'extraire les ressources de Payerbox. Le pipeline lit via une interface
source commune, de sorte que l'une ou l'autre pourrait l'alimenter; nous utilisons l'exportation
en masse `$export`.

- **L'API REST FHIR.** Parcourir avec des requêtes de recherche ordinaires. C'est une bonne
  approche pour une tranche restreinte et ciblée, mais extraire des types de ressources entiers à
  grande échelle implique de nombreux allers-retours, et les données peuvent changer entre les pages.
- **L'opération d'exportation en masse FHIR `$export`.** Cette opération
  s'exécute [de façon asynchrone](https://www.health-samurai.io/articles/unified-fhir-async-operations-pattern)
  et diffuse tout en NDJSON en un seul passage, sans la dérive entre pages propre à la pagination
  de l'API en direct. Elle est conçue pour cela, c'est donc ce que nous utilisons.

`$export` est l'opération standard [FHIR Bulk Data Access](https://hl7.org/fhir/uv/bulkdata/),
avec `_typeFilter` pour pousser des filtres sélectifs dans l'exportation et `+gzip` sur
`_outputFormat` pour réduire la taille du vidage. Avec `_typeFilter`, le serveur n'envoie que
les ressources qui importent :

```
GET /fhir/$export
  ?_outputFormat=application/fhir+ndjson+gzip
  &_type=InsurancePlan,Organization,Practitioner,PractitionerRole,Location,OrganizationAffiliation
  &_typeFilter=InsurancePlan?status=active&_profile=.../plannet-InsurancePlan&_id=...
  &_typeFilter=PractitionerRole?active=true&_profile=.../plannet-PractitionerRole&network=...
  &_typeFilter=OrganizationAffiliation?active=true&_profile=.../plannet-OrganizationAffiliation&network=...
```

Lancé avec l'en-tête de requête `Prefer: respond-async`, `$export` retourne `202 Accepted`
avec un en-tête de réponse `Content-Location` : l'URL de statut que vous interrogez jusqu'à ce
que les fichiers NDJSON soient prêts. Les régimes, les rôles de prestataires et les affiliations
reviennent déjà restreints aux ressources Plan-Net actives et en réseau. Les enfants qu'ils
référencent arrivent en intégralité, de sorte qu'un enregistrement conservé ne pointe jamais vers
quelque chose de supprimé.

## Filtrage vers le sous-ensemble du réseau

L'exportation `$export` constituait le premier filtre : `_typeFilter` restreint les parents sur le
serveur FHIR. Un traitement côté code résout ensuite les enfants : il parcourt le graphe (régime →
réseau → organisations et prestataires affiliés → leurs emplacements), ne conserve un enfant que
si un parent conservé le référence, et élimine le reste. Ce graphe correspond aux références que
PDex Plan-Net définit entre les six ressources :

<div class="narrow" style="display: flex; justify-content: center">

```mermaid
flowchart TD
    IP["InsurancePlan"] -->|network| NET["Organization<br/>(Network, type=ntwk)"]
    PR["PractitionerRole"] -->|network| NET
    PR -->|practitioner| PRAC["Practitioner"]
    PR -->|location| LOC["Location"]
    OA["OrganizationAffiliation"] -->|network| NET
    OA -->|organization| FAC["Organization<br/>(Facility, type=fac)"]
    OA -->|location| LOC
```

</div>

*Les références entre les ressources Plan-Net. `_typeFilter` restreint les parents (InsurancePlan,
PractitionerRole, OrganizationAffiliation) sur le serveur FHIR; le traitement par code conserve
ensuite les enfants qu'ils référencent.*

## Regroupement et publication

Les ressources conservées sont regroupées dans des [Bundles](https://hl7.org/fhir/R4/bundle.html) FHIR
(`type: collection`), répartis par type de ressource dans des fichiers numérotés (`PractitionerRole-001.json`,
`-002.json`, etc.); chaque ressource conserve sa valeur source `meta.lastUpdated`, que le guide
associe à la date de dernière mise à jour de chaque enregistrement. CMS s'attend à cette
répartition : il exige un fichier d'index par contrat, un
manifeste de chaque URL de fichier que le robot d'exploration lit en premier :

```json
{
  "provider_urls": [
    "https://.../H9999/2026/InsurancePlan-001.json",
    "https://.../H9999/2026/PractitionerRole-001.json",
    "https://.../H9999/2026/PractitionerRole-002.json",
    "https://.../H9999/2026/Organization-001.json"
  ]
}
```

La répartition maintient chaque fichier à une taille gérable pour le robot d'exploration; le
pipeline passe au fichier numéroté suivant lorsqu'un Bundle devient trop volumineux ou dépasse
un nombre limite d'entrées.

La publication obéit à une règle stricte : un parcours ne doit jamais tomber sur un répertoire
à moitié écrit. Un fichier comme `Organization-042.json` contient des ressources différentes
d'une exécution à l'autre; l'écraser en place pendant que l'ancien manifeste pointe encore vers
lui constitue une lecture incohérente. La publication est donc un remplacement atomique : effacer
les anciens fichiers, téléverser les nouveaux Bundles, puis téléverser `index.json` en dernier.
Ce dernier téléversement est la seule étape qui rend la nouvelle version active; ainsi, un robot
d'exploration en cours de parcours voit le répertoire d'hier, celui d'aujourd'hui, ou un bref
404 durant le remplacement, mais jamais un mélange à moitié écrit. Une exécution qui échoue en
cours de route déclenche une alerte. Il n'y a pas de reprise partielle : la prochaine exécution
reconstruit simplement l'intégralité du répertoire depuis le début.

## Service au robot d'exploration

CMS recommande les requêtes conditionnelles, et le point d'accès les prend en charge. Chaque
fichier porte un `ETag` (une empreinte de son contenu qui change lorsque le fichier est modifié)
et un horodatage `Last-Modified`; le robot d'exploration les enregistre et, lors de sa prochaine
visite, ne récupère un fichier que si son `ETag` a changé. Les fichiers inchangés reviennent sous
la forme `304 Not Modified` sans corps :

```http
GET /mpf-provider-directory/H9999/2026/Organization-042.json
If-None-Match: "a1b2c3"        # the ETag the crawler received on its last fetch
→ 304 Not Modified             # unchanged since then, so no body
```

Pour un répertoire composé de milliers de fichiers Bundle qui changent à peine d'un jour à
l'autre, c'est la différence entre un parcours lourd et un parcours léger. Cela explique aussi
la publication atomique : l'`ETag` doit changer exactement au moment où le contenu change.

Ce point d'accès agit comme un mandataire vers un compartiment privé plutôt que d'exposer le
compartiment lui-même, et ne nécessite aucune authentification : le répertoire Plan-Net est
conçu pour être librement accessible, sans inscription du client.

## Vous souhaitez en construire un vous-même ?

Sa construction représente un effort d'ingénierie relativement modeste pour quiconque travaille
régulièrement avec des serveurs FHIR : une tâche quotidienne d'exportation-filtrage-regroupement-publication.
Mais le soin réside dans l'exactitude à chaque exécution, et l'ensemble suppose que des données
Plan-Net correctes se trouvent déjà dans le serveur FHIR. Les y introduire est un projet à part
entière. La qualité des données doit être surveillée en continu, en validant le jeu de ressources
PDex Plan-Net par rapport aux critères MPF de CMS.

Ce répertoire est livré sous forme de module optionnel de
[Payerbox](https://www.health-samurai.io/cms-0057-f), la plateforme de Health Samurai pour les
exigences CMS des payeurs, qui fournit également les API CMS-0057-F attendues pour 2027.

> Vous construisez un répertoire de prestataires pour Medicare Plan Finder ?
> [Parlez à notre équipe](https://www.health-samurai.io/company#contact-form) pour commencer.

## Références

- [Règle finale CMS-4208-F2](https://www.federalregister.gov/documents/2025/09/19/2025-18236/medicare-and-medicaid-programs-contract-year-2026-policy-and-technical-changes-to-the-medicare)
- [L'exigence de répertoire de prestataires MPF, expliquée](https://www.health-samurai.io/articles/mpf-provider-directory-medicare-advantage-cms-4208-f2)
- [Guide technique de mise en œuvre du répertoire de prestataires MA de CMS](https://www.cms.gov/files/zip/mpf-ma-provider-directory-technical-guide-02182026-final-zip.zip)
- [Da Vinci PDex Plan-Net IG](https://hl7.org/fhir/us/davinci-pdex-plan-net/)
- [FHIR Bulk Data Access](https://hl7.org/fhir/uv/bulkdata/)