Installation
composer require qrcommunication/rppsapi
guzzlehttp/guzzle est incluse automatiquement.
Quick Start
Deux patterns principaux : récupérer un profil complet par numéro RPPS, ou rechercher des praticiens par nom, prénom et/ou code postal.
<?php
use QrCommunication\RppsApi\RppsClient;
use QrCommunication\RppsApi\RppsClientOptions;
use QrCommunication\RppsApi\Dto\RppsSearchCriteria;
use QrCommunication\RppsApi\Exception\RppsException;
$client = new RppsClient(new RppsClientOptions(
fhirApiKey: $_ENV['FHIR_API_KEY'] ?? null,
));
// ── Profil complet par numéro RPPS ─────────────────────────────
try {
$profil = $client->getByRpps('10005173140');
echo $profil->identite->civiliteExercice->libelle . ' ';
echo $profil->identite->prenomExercice . ' ' . $profil->identite->nomExercice . "\n";
echo 'Profession : ' . $profil->profession->libelle . "\n";
foreach ($profil->activites as $activite) {
echo ' Structure : ' . $activite->structure->raisonSociale . "\n";
echo ' Adresse : ' . $activite->structure->adresse->libelleCommune . "\n";
}
foreach ($profil->diplomesEtAutorisations as $da) {
if ($da->diplome) {
echo ' Diplôme : ' . $da->diplome->libelle . "\n";
}
}
foreach ($profil->savoirFaire as $sf) {
echo ' Savoir-faire : ' . $sf->savoirFaire->libelle . "\n";
}
foreach ($profil->messageriesMssante as $mss) {
echo ' MSSanté : ' . $mss->adresseBal . "\n";
}
// Statut des sources interrogées
foreach ($profil->sources as $source) {
$status = $source->success ? 'OK' : 'ERREUR';
echo " [{$status}] {$source->source->value} — {$source->durationMs}ms\n";
}
} catch (RppsException $e) {
echo 'Erreur RPPS : ' . $e->getMessage() . "\n";
}
// ── Recherche par critères ──────────────────────────────────────
$results = $client->search(new RppsSearchCriteria(
nom: 'DUPONT',
codePostal: '75',
));
echo "Total trouvé : {$results->total}\n";
echo "Durée : {$results->durationMs}ms\n";
foreach ($results->results as $r) {
echo " {$r->civilite} {$r->prenomExercice} {$r->nomExercice}";
echo " — {$r->profession->libelle}";
echo " — {$r->libelleCommune}\n";
}
// ── Pagination ──────────────────────────────────────────────────
$page2 = $client->search(new RppsSearchCriteria(nom: 'MARTIN'), page: 2, pageSize: 50);
Configuration
RppsClientOptions est un objet readonly passé au constructeur de RppsClient. Tous les paramètres ont des valeurs par défaut fonctionnelles.
use QrCommunication\RppsApi\RppsClientOptions;
$options = new RppsClientOptions(
fhirBaseUrl: 'https://gateway.api.esante.gouv.fr/fhir/v2', // URL de base FHIR ANS
fhirApiKey: 'votre-cle-esante-api-key', // Clé API Gravitee (optionnel)
tabularBaseUrl: 'https://tabular-api.data.gouv.fr/api/resources', // URL Tabular data.gouv.fr
timeout: 30.0, // Timeout Guzzle en secondes
tabularPageSize: 100, // Taille de page pour les requêtes Tabular
disabledSources: ['fhir', 'mssante'], // Sources à désactiver
);
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
fhirBaseUrl |
string | 'https://gateway.api.esante.gouv.fr/fhir/v2' |
URL de base de l'API FHIR ANS |
fhirApiKey |
string|null | null |
Clé API Gravitee (ESANTE-API-KEY). Sans cette clé, la source FHIR est ignorée. |
tabularBaseUrl |
string | 'https://tabular-api.data.gouv.fr/api/resources' |
URL de base de l'API Tabular data.gouv.fr |
timeout |
float | 30.0 |
Timeout en secondes pour les requêtes HTTP Guzzle |
tabularPageSize |
int | 100 |
Nombre de lignes par page pour les requêtes Tabular (utilisé par search) |
disabledSources |
string[] | [] |
Liste des sources à désactiver. Valeurs acceptées : 'fhir', 'personne-activite', 'diplomes', 'savoir-faire', 'carte-cps', 'mssante' |
Méthodes
getByRpps
PUBLICgetByRpps(string $rpps): RppsFullProfile
Récupère le profil complet d'un praticien à partir de son numéro RPPS (11 chiffres). Interroge toutes les sources activées (identité, activités, diplômes, savoir-faire, cartes CPS, messageries MSSanté, FHIR) et retourne un objet agrégé.
| Paramètre | Type | Description |
|---|---|---|
$rpps |
string | Numéro RPPS à 11 chiffres. Les espaces en début/fin sont ignorés. |
RppsException si le numéro RPPS ne contient pas exactement 11 chiffres.
$profil = $client->getByRpps('10005173140');
// Retourne RppsFullProfile
search
PUBLICsearch(RppsSearchCriteria $criteria, int $page = 1, ?int $pageSize = null): RppsSearchResponse
Recherche des professionnels de santé par critères. Au moins un critère est obligatoire. La recherche est partielle et insensible à la casse (contains). Pour codePostal, une valeur de 5 caractères utilise un filtre exact.
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
$criteria |
RppsSearchCriteria | — | Critères de recherche (nom, prénom, code postal) |
$page |
int | 1 |
Numéro de page (commence à 1) |
$pageSize |
int|null | null |
Résultats par page. Si null, utilise tabularPageSize des options. |
RppsException si aucun critère n'est fourni.
$results = $client->search(
new RppsSearchCriteria(nom: 'DUPONT', prenom: 'JEAN', codePostal: '75001'),
page: 1,
pageSize: 50,
);
// Retourne RppsSearchResponse
validateRpps
STATICstatic validateRpps(string $rpps): string
Valide un numéro RPPS. Retourne le numéro nettoyé (sans espaces) si valide. Lance RppsException si le format est incorrect (doit contenir exactement 11 chiffres).
// Validation statique (pas besoin d'instancier RppsClient)
$clean = RppsClient::validateRpps('10005173140'); // '10005173140'
$clean = RppsClient::validateRpps(' 10005173140 '); // '10005173140' (trim)
// Lance RppsException :
RppsClient::validateRpps('123'); // Trop court
RppsClient::validateRpps('1234567890A'); // Contient une lettre
DTOs
Tous les DTOs sont des classes final readonly. Les valeurs manquantes sont des chaînes vides '' ou des instances par défaut (jamais null), sauf indication contraire.
RppsFullProfile
Profil agrégé retourné par getByRpps().
| Propriété | Type | Description |
|---|---|---|
rpps | string | Numéro RPPS à 11 chiffres |
identificationNationale | string | Identifiant national PP |
identite | Identite | Civilité, nom et prénom d'exercice |
profession | Profession | Code et libellé profession, catégorie professionnelle |
activites | Activite[] | Activités et structures rattachées (une entrée par structure avec identifiant technique) |
diplomesEtAutorisations | DiplomeEtAutorisation[] | Diplômes obtenus et autorisations d'exercice |
savoirFaire | SavoirFaire[] | Spécialités et compétences déclarées |
cartesCps | CarteCps[] | Cartes CPS (Carte de Professionnel de Santé) |
messageriesMssante | MessagerieMssante[] | Adresses de messagerie sécurisée MSSanté |
fhir | FhirData | Données FHIR (Practitioner + PractitionerRoles). Vide si pas de clé API. |
sources | SourceStatus[] | Statut de chaque source interrogée (succès, durée, erreur éventuelle) |
queriedAt | string | Horodatage ISO 8601 de la requête |
totalDurationMs | float | Durée totale d'exécution en millisecondes |
Identite
Identité civile d'exercice du praticien.
| Propriété | Type | Description |
|---|---|---|
civilite | CodeLabel | Civilité administrative (ex : M / M.) |
civiliteExercice | CodeLabel | Civilité d'exercice (ex : DR / Docteur) |
nomExercice | string | Nom d'exercice |
prenomExercice | string | Prénom d'exercice |
Profession
Profession déclarée dans le RPPS.
| Propriété | Type | Description |
|---|---|---|
code | string | Code profession (ex : 10) |
libelle | string | Libellé profession (ex : Médecin) |
categorie | CodeLabel | Catégorie professionnelle (code + libellé) |
Activite
Activité professionnelle rattachée à une structure.
| Propriété | Type | Description |
|---|---|---|
modeExercice | CodeLabel | Mode d'exercice (ex : libéral, salarié) |
savoirFaire | CodeLabel | Savoir-faire associé à l'activité |
typeSavoirFaire | CodeLabel | Type de savoir-faire |
secteurActivite | CodeLabel | Secteur d'activité |
sectionTableauPharmaciens | CodeLabel | Section du tableau des pharmaciens |
role | CodeLabel | Rôle dans la structure |
genreActivite | CodeLabel | Genre d'activité |
autoriteEnregistrement | string | Autorité d'enregistrement |
structure | Structure | Structure d'exercice rattachée |
Structure
Établissement ou cabinet d'exercice.
| Propriété | Type | Description |
|---|---|---|
identifiantTechnique | string | Identifiant technique de la structure |
raisonSociale | string | Raison sociale du site |
enseigneCommerciale | string | Enseigne commerciale du site |
numeroSiret | string | Numéro SIRET |
numeroSiren | string | Numéro SIREN |
numeroFinessSite | string | Numéro FINESS du site |
numeroFinessEtablissementJuridique | string | Numéro FINESS de l'établissement juridique |
telephone | string | Téléphone principal |
telephone2 | string | Téléphone secondaire |
telecopie | string | Numéro de télécopie |
email | string | Adresse e-mail de la structure |
codeDepartement | string | Code département |
libelleDepartement | string | Libellé département |
ancienIdentifiant | string | Ancien identifiant de la structure |
adresse | AdresseStructure | Adresse postale complète |
AdresseStructure
Adresse postale normalisée d'une structure.
| Propriété | Type | Description |
|---|---|---|
complementDestinataire | string | Complément destinataire |
complementPointGeographique | string | Complément point géographique |
numeroVoie | string | Numéro de voie |
indiceRepetitionVoie | string | Indice de répétition de voie (bis, ter…) |
codeTypeVoie | string | Code type de voie |
libelleTypeVoie | string | Libellé type de voie (Rue, Avenue…) |
libelleVoie | string | Nom de la voie |
mentionDistribution | string | Mention de distribution |
bureauCedex | string | Bureau cedex |
codePostal | string | Code postal |
codeCommune | string | Code INSEE commune |
libelleCommune | string | Libellé commune |
codePays | string | Code pays |
libellePays | string | Libellé pays |
DiplomeEtAutorisation
Diplôme obtenu ou autorisation d'exercice. Chaque entrée représente soit un diplôme, soit une autorisation (jamais les deux à la fois). Les propriétés sont null si la valeur est absente.
| Propriété | Type | Description |
|---|---|---|
typeDiplome | CodeLabel|null | Type de diplôme obtenu (non nul si diplôme) |
diplome | CodeLabel|null | Diplôme obtenu (code + libellé) |
typeAutorisation | CodeLabel|null | Type d'autorisation (non nul si autorisation) |
disciplineAutorisation | CodeLabel|null | Discipline de l'autorisation |
SavoirFaire
Spécialité ou compétence déclarée dans le RPPS.
| Propriété | Type | Description |
|---|---|---|
profession | CodeLabel | Profession associée au savoir-faire |
categorieProfessionnelle | CodeLabel | Catégorie professionnelle |
typeSavoirFaire | CodeLabel | Type de savoir-faire (spécialité, capacité…) |
savoirFaire | CodeLabel | Savoir-faire (code + libellé) |
CarteCps
Carte de Professionnel de Santé (CPS) associée au praticien.
| Propriété | Type | Description |
|---|---|---|
typeCarte | CodeLabel | Type de carte CPS |
numeroCarte | string | Numéro de carte |
identifiantNationalCarte | string | Identifiant national contenu dans la carte |
dateDebutValidite | string | Date de début de validité |
dateFinValidite | string | Date de fin de validité |
dateOpposition | string | Date d'opposition (si carte opposée) |
dateMiseAJour | string | Date de mise à jour |
MessagerieMssante
Boîte aux lettres de messagerie sécurisée MSSanté.
| Propriété | Type | Description |
|---|---|---|
typeBal | string | Type de BAL MSSanté |
adresseBal | string | Adresse MSSanté (ex : jean.dupont@medecin.mssante.fr) |
dematerialisation | bool | true si la dématérialisation est activée |
savoirFaire | CodeLabel | Savoir-faire associé à cette boîte |
structureIdentifiant | string | Identifiant de la structure rattachée |
structureTypeIdentifiant | string | Type d'identifiant de la structure |
serviceRattachement | string | Service de rattachement |
raisonSociale | string | Raison sociale de la structure BAL |
codePostal | string | Code postal de la structure BAL |
departement | string | Département de la structure BAL |
FhirData
Données FHIR brutes retournées par l'API ANS. Disponibles uniquement si fhirApiKey est configurée et que la source n'est pas désactivée.
| Propriété | Type | Description |
|---|---|---|
practitioner | array|null | Ressource FHIR Practitioner brute (décodée depuis JSON) |
practitionerRoles | array[] | Tableau de ressources FHIR PractitionerRole (jusqu'à 50) |
CodeLabel
Paire code/libellé réutilisée dans tous les DTOs pour les données de référentiel (civilité, profession, mode d'exercice, etc.).
| Propriété | Type | Description |
|---|---|---|
code | string | Code de la valeur (ex : '10', 'DR') |
libelle | string | Libellé humain (ex : 'Médecin', 'Docteur') |
$profil->profession->libelle; // 'Médecin'
$profil->profession->code; // '10'
$profil->identite->civiliteExercice->libelle; // 'Docteur'
$arr = $profil->profession->categorie->toArray();
// ['code' => 'C', 'libelle' => 'Civil']
SourceStatus
Statut d'exécution d'une source de données.
| Propriété | Type | Description |
|---|---|---|
source | SourceName | Identifiant de la source (enum) |
success | bool | true si la requête a réussi |
rowCount | int | Nombre de lignes retournées |
durationMs | float | Durée d'exécution en millisecondes |
error | string|null | Message d'erreur si success === false, ou raison de désactivation |
RppsSearchCriteria
Critères passés à search(). Au moins un critère non nul et non vide est requis.
| Propriété | Type | Description |
|---|---|---|
nom | string|null | Nom d'exercice (recherche partielle contains) |
prenom | string|null | Prénom d'exercice (recherche partielle contains) |
codePostal | string|null | Code postal — filtre exact si 5 caractères, sinon contains |
RppsSearchResponse
Réponse paginée retournée par search().
| Propriété | Type | Description |
|---|---|---|
results | RppsSearchResult[] | Résultats de la page courante (dédupliqués par RPPS) |
total | int | Nombre total de résultats (toutes pages) |
page | int | Numéro de page courant |
pageSize | int | Nombre de résultats par page |
durationMs | float | Durée d'exécution en millisecondes |
criteria | RppsSearchCriteria | Critères utilisés pour cette requête |
RppsSearchResult
Résultat allégé d'une recherche (moins de champs que RppsFullProfile). Pour un profil complet, appeler getByRpps($result->rpps).
| Propriété | Type | Description |
|---|---|---|
rpps | string | Numéro RPPS |
identificationNationale | string | Identifiant national PP |
civilite | string | Libellé civilité d'exercice |
nomExercice | string | Nom d'exercice |
prenomExercice | string | Prénom d'exercice |
profession | CodeLabel | Profession (code + libellé) |
categorieProfessionnelle | CodeLabel | Catégorie professionnelle |
modeExercice | CodeLabel | Mode d'exercice |
savoirFaire | CodeLabel | Savoir-faire principal |
raisonSociale | string | Raison sociale de la structure |
codePostal | string | Code postal de la structure |
libelleCommune | string | Commune de la structure |
departement | string | Département de la structure |
Sources de données
Le SDK agrège 6 sources interrogées lors de chaque appel à getByRpps(). Chaque source correspond à une ressource sur data.gouv.fr ou à l'API FHIR ANS.
| Source | Enum SourceName |
Resource ID | Données fournies |
|---|---|---|---|
personne-activite |
PersonneActivite |
fffda7e9-0ea2-4c35-bba0-4496f3af935d |
Identité, profession, activités, structures |
diplomes |
Diplomes |
41ae70ac-90c8-4c4e-8644-4ef1b100f045 |
Diplômes obtenus et autorisations d'exercice |
savoir-faire |
SavoirFaire |
fb55f15f-bd61-4402-b551-51ef387f2fab |
Spécialités et compétences déclarées |
carte-cps |
CarteCps |
210eb05e-564b-42be-994a-d1800b63e9b7 |
Cartes CPS (type, numéro, validité) |
mssante |
Mssante |
afe01105-d9a1-41fe-921f-e40ea48b2ba6 |
Messageries sécurisées MSSanté |
fhir |
Fhir |
— API FHIR ANS | Practitioner + PractitionerRoles FHIR (clé API requise) |
tabular-api.data.gouv.fr/api/resources/{resource-id}/data/ avec un filtre exact sur Identifiant PP (colonne RPPS). La source FHIR utilise gateway.api.esante.gouv.fr/fhir/v2/Practitioner.
Enum SourceName
Backed enum PHP utilisé dans SourceStatus::$source et RppsClientOptions::$disabledSources.
use QrCommunication\RppsApi\Enum\SourceName;
SourceName::Fhir->value; // 'fhir'
SourceName::PersonneActivite->value; // 'personne-activite'
SourceName::Diplomes->value; // 'diplomes'
SourceName::SavoirFaire->value; // 'savoir-faire'
SourceName::CarteCps->value; // 'carte-cps'
SourceName::Mssante->value; // 'mssante'
| Case | Valeur |
|---|---|
SourceName::Fhir | 'fhir' |
SourceName::PersonneActivite | 'personne-activite' |
SourceName::Diplomes | 'diplomes' |
SourceName::SavoirFaire | 'savoir-faire' |
SourceName::CarteCps | 'carte-cps' |
SourceName::Mssante | 'mssante' |
Gestion des erreurs
Le SDK lance RppsException (étend \RuntimeException) dans deux cas : numéro RPPS invalide ou absence de critères de recherche. Les erreurs réseau ou d'API sont capturées silencieusement et consignées dans RppsFullProfile::$sources avec success === false.
use QrCommunication\RppsApi\Exception\RppsException;
// ── Cas 1 : RPPS invalide ───────────────────────────────────────
try {
$profil = $client->getByRpps('123');
} catch (RppsException $e) {
// "Numéro RPPS invalide : "123". Le RPPS doit contenir exactement 11 chiffres."
echo $e->getMessage();
}
// ── Cas 2 : aucun critère de recherche ─────────────────────────
try {
$results = $client->search(new RppsSearchCriteria());
} catch (RppsException $e) {
// "Au moins un critère de recherche est obligatoire (nom, prenom ou codePostal)."
echo $e->getMessage();
}
// ── Cas 3 : erreur source (silencieuse, consultable après coup) ─
$profil = $client->getByRpps('10005173140');
foreach ($profil->sources as $source) {
if (!$source->success) {
// La source a échoué, mais le reste du profil est disponible
echo "[{$source->source->value}] ERREUR : {$source->error}\n";
}
}
Exemples
Profil complet par RPPS
$client = new RppsClient(new RppsClientOptions(
fhirApiKey: getenv('ESANTE_API_KEY'),
));
$profil = $client->getByRpps('10005173140');
// Identité
echo $profil->identite->civiliteExercice->libelle; // 'Docteur'
echo $profil->identite->prenomExercice; // 'JEAN'
echo $profil->identite->nomExercice; // 'DUPONT'
// Profession
echo $profil->profession->libelle; // 'Médecin'
echo $profil->profession->code; // '10'
// Structures d'exercice
foreach ($profil->activites as $activite) {
echo $activite->structure->raisonSociale . "\n";
echo $activite->structure->adresse->libelleCommune . ' ';
echo $activite->structure->adresse->codePostal . "\n";
echo $activite->modeExercice->libelle . "\n";
}
// Diplômes
foreach ($profil->diplomesEtAutorisations as $da) {
if ($da->diplome !== null) {
echo '[Diplôme] ' . $da->diplome->libelle . "\n";
}
if ($da->typeAutorisation !== null) {
echo '[Autorisation] ' . $da->typeAutorisation->libelle . "\n";
}
}
// Messageries MSSanté
foreach ($profil->messageriesMssante as $mss) {
echo $mss->adresseBal . ' (' . $mss->typeBal . ")\n";
}
// FHIR (si clé configurée)
if ($profil->fhir->practitioner !== null) {
echo 'ID FHIR : ' . $profil->fhir->practitioner['id'] . "\n";
echo count($profil->fhir->practitionerRoles) . " rôle(s) FHIR\n";
}
Recherche et enrichissement
// Recherche initiale
$results = $client->search(new RppsSearchCriteria(
nom: 'MARTIN',
codePostal: '69',
));
echo "Trouvé : {$results->total} praticiens\n";
// Pagination
if ($results->total > $results->pageSize) {
$totalPages = ceil($results->total / $results->pageSize);
echo "Affichage page 1/{$totalPages}\n";
}
// Afficher les résultats
foreach ($results->results as $r) {
echo "{$r->civilite} {$r->prenomExercice} {$r->nomExercice}";
echo " — {$r->profession->libelle} ({$r->modeExercice->libelle})";
echo " — {$r->libelleCommune} {$r->codePostal}\n";
}
// Enrichir le premier résultat avec le profil complet
if (!empty($results->results)) {
$profilComplet = $client->getByRpps($results->results[0]->rpps);
}
Désactiver des sources
Utile pour accélérer les requêtes quand certaines données ne sont pas nécessaires.
// Identité + profession uniquement (sans CPS, MSSanté, FHIR)
$clientLeger = new RppsClient(new RppsClientOptions(
disabledSources: ['carte-cps', 'mssante', 'fhir'],
));
// Identité seulement (désactiver tout sauf personne-activite)
$clientMinimal = new RppsClient(new RppsClientOptions(
disabledSources: ['carte-cps', 'mssante', 'fhir', 'diplomes', 'savoir-faire'],
));
// Vérifier quelles sources ont été désactivées dans la réponse
$profil = $clientLeger->getByRpps('10005173140');
foreach ($profil->sources as $src) {
if (!$src->success && $src->error === 'disabled') {
echo "[DESACTIVE] {$src->source->value}\n";
}
}
Configuration FHIR
L'API FHIR ANS est gratuite mais nécessite une clé d'API Gravitee. Sans clé, la source FHIR est ignorée (pas d'erreur, le profil reste complet sur les autres sources).
- Créer un compte sur portal.api.esante.gouv.fr
- Créer une application
- S'abonner à l'offre API Annuaire Santé en libre accès
- Récupérer la clé
ESANTE-API-KEY
$client = new RppsClient(new RppsClientOptions(
fhirApiKey: $_ENV['ESANTE_API_KEY'], // ou getenv('ESANTE_API_KEY')
));
ESANTE-API-KEY à chaque requête vers gateway.api.esante.gouv.fr/fhir/v2. Elle n'est jamais transmise aux APIs Tabular.
Skill AI
Le SDK inclut un skill AI qui installe une documentation complète directement dans votre agent de code (Claude Code, Cursor, Codex, Windsurf, Cline, Aider, Gemini CLI).
bash vendor/qrcommunication/rppsapi/skill/install.sh
Installation manuelle :
cp -r vendor/qrcommunication/rppsapi/skill ~/.claude/skills/sdk-rpps