API DocDirectory.ai
Une API REST pour lire et gérer par programmation les fiches médecin et cabinet que vous possédez. Toutes les requêtes et réponses sont au format JSON.
Authorizationde chaque requête. L'accès à l'API doit d'abord être activé pour votre compte (visible sur cette même page du tableau de bord) ; tant qu'il ne l'est pas, toute requête répond 401 même avec un jeton valide.curl https://docdirectory.ai/api/v1/me \
-H "Authorization: Bearer sk_votre_jeton"Une requête sans jeton, avec un jeton invalide, ou pour un compte sans accès activé, reçoit :
{ "error": "Jeton d'API invalide ou manquant." }Ressources
/doctors. La liste (GET /api/v1/doctors) ne renvoie que id, slug, full_name, city; l'objet complet ci-dessous n'apparaît que sur les endpoints par identifiant.| Champ | Type | Description |
|---|---|---|
| id | string (uuid) | Identifiant, généré à la création. |
| slug | string | Segment d'URL public (/doctors/:slug). Généré une fois à la création à partir de full_name, jamais recalculé. |
| full_name | string | Nom complet. Obligatoire à la création (2 à 200 caractères). |
| bio | string | null | Présentation libre (4000 caractères max). |
| address | string | null | Adresse (200 caractères max). |
| city | string | null | Ville (100 caractères max). |
| postal_code | string | null | Code postal (20 caractères max). |
| google_url | string | null | Fiche Google |
| doctolib_url | string | null | Profil Doctolib |
| website_url | string | null | Site web personnel |
| instagram_url | string | null | |
| facebook_url | string | null | |
| tiktok_url | string | null | TikTok |
| linkedin_url | string | null | |
| specialities | Array<{ id, name, slug }> | Spécialités associées. En écriture, fournissez speciality_ids (voir ci-dessous) plutôt que ce tableau. |
| clinics | Array<{ id, slug, name }> | Cabinets associés — lecture seule sur cet objet. Gérez l'association via les endpoints de la section « Association médecin ↔ cabinet ». |
/clinics. La liste (GET /api/v1/clinics) ne renvoie que id, slug, name, city; l'objet complet ci-dessous n'apparaît que sur les endpoints par identifiant.| Champ | Type | Description |
|---|---|---|
| id | string (uuid) | Identifiant, généré à la création. |
| slug | string | Segment d'URL public (/clinics/:slug). Généré une fois à la création à partir de name, jamais recalculé. |
| name | string | Nom du cabinet. Obligatoire à la création (2 à 200 caractères). |
| bio | string | null | Présentation libre (4000 caractères max). |
| address | string | null | Adresse (200 caractères max). |
| city | string | null | Ville (100 caractères max). |
| postal_code | string | null | Code postal (20 caractères max). |
| google_url | string | null | Fiche Google |
| doctolib_url | string | null | Profil Doctolib |
| website_url | string | null | Site web personnel |
| instagram_url | string | null | |
| facebook_url | string | null | |
| tiktok_url | string | null | TikTok |
| linkedin_url | string | null | |
| specialities | Array<{ id, name, slug }> | Spécialités associées. En écriture, fournissez speciality_ids (voir ci-dessous) plutôt que ce tableau. |
| doctors | Array<{ id, slug, full_name }> | Médecins associés — lecture seule sur cet objet. Gérez l'association via les endpoints ci-dessous. |
POST/PATCHmais absents des objets renvoyés (les spécialités sont renvoyées sous forme d'objets { id, name, slug } dans le champ specialities, pas sous forme d'identifiants).| Champ | Type | Description |
|---|---|---|
| speciality_ids | string[] (uuids), facultatif | Sur POST : spécialités à associer à la création (facultatif, défaut aucune). Sur PATCH : n'est appliqué que si la clé est présente dans le corps de la requête — omettez-la pour ne pas toucher aux spécialités existantes, ou envoyez un tableau (y compris vide) pour les remplacer entièrement. Les identifiants doivent exister dans /specialities (consultez le tableau de bord pour les lister, il n'y a pas encore d'endpoint dédié). |
Endpoints
Compte
/api/v1/me{
"data": {
"id": "5b1e...-uuid",
"email": "[email protected]",
"full_name": "Dr. Camille Dupont"
}
}Médecins
/api/v1/doctors{
"data": [
{
"id": "3f2a...-uuid",
"slug": "camille-dupont",
"full_name": "Dr. Camille Dupont",
"city": "Lyon"
}
]
}/api/v1/doctorsCorps de la requête
{
"full_name": "Dr. Camille Dupont",
"bio": "…",
"city": "Lyon",
"doctolib_url": "https://…",
"speciality_ids": ["8b86be3c-…"]
}Réponse
{ "data": {
"id": "3f2a...-uuid",
"slug": "camille-dupont",
"full_name": "Dr. Camille Dupont",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": "https://…",
"website_url": null,
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"clinics": [{ "id": "…", "slug": "…", "name": "Cabinet du Parc" }]
} }/api/v1/doctors/:id{ "data": {
"id": "3f2a...-uuid",
"slug": "camille-dupont",
"full_name": "Dr. Camille Dupont",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": "https://…",
"website_url": null,
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"clinics": [{ "id": "…", "slug": "…", "name": "Cabinet du Parc" }]
} }/api/v1/doctors/:idCorps de la requête
{
"full_name": "Dr. Camille Dupont",
"city": "Paris",
"speciality_ids": []
}Réponse
{ "data": {
"id": "3f2a...-uuid",
"slug": "camille-dupont",
"full_name": "Dr. Camille Dupont",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": "https://…",
"website_url": null,
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"clinics": [{ "id": "…", "slug": "…", "name": "Cabinet du Parc" }]
} }/api/v1/doctors/:idHTTP/1.1 204 No ContentCabinets
/api/v1/clinics{
"data": [
{
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"city": "Lyon"
}
]
}/api/v1/clinicsCorps de la requête
{
"name": "Cabinet du Parc",
"city": "Lyon",
"speciality_ids": ["8b86be3c-…"]
}Réponse
{ "data": {
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": null,
"website_url": "https://…",
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"doctors": [{ "id": "…", "slug": "camille-dupont", "full_name": "Dr. Camille Dupont" }]
} }/api/v1/clinics/:id{ "data": {
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": null,
"website_url": "https://…",
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"doctors": [{ "id": "…", "slug": "camille-dupont", "full_name": "Dr. Camille Dupont" }]
} }/api/v1/clinics/:idCorps de la requête
{
"name": "Cabinet du Parc",
"city": "Paris"
}Réponse
{ "data": {
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": null,
"website_url": "https://…",
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"doctors": [{ "id": "…", "slug": "camille-dupont", "full_name": "Dr. Camille Dupont" }]
} }/api/v1/clinics/:idHTTP/1.1 204 No ContentAssociation médecin ↔ cabinet
Un médecin peut exercer dans plusieurs cabinets, et un cabinet regrouper plusieurs médecins. Lier une fiche nécessite de posséder les deux côtés du lien ; délier ne nécessite d'en posséder qu'un seul (utile si vous ne gérez qu'un côté de la relation).
/api/v1/clinics/:id/doctorsCorps de la requête
{ "doctor_id": "3f2a...-uuid" }Réponse
{ "data": {
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": null,
"website_url": "https://…",
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"doctors": [{ "id": "…", "slug": "camille-dupont", "full_name": "Dr. Camille Dupont" }]
} }/api/v1/clinics/:id/doctors/:doctorIdHTTP/1.1 204 No Contentcurl -X POST https://docdirectory.ai/api/v1/clinics/9c4d...-uuid/doctors \
-H "Authorization: Bearer sk_votre_jeton" \
-H "Content-Type: application/json" \
-d '{ "doctor_id": "3f2a...-uuid" }'La réponse est le cabinet à jour, doctors incluant désormais ce médecin :
{ "data": {
"id": "9c4d...-uuid",
"slug": "cabinet-du-parc",
"name": "Cabinet du Parc",
"bio": "…",
"address": "…",
"city": "Lyon",
"postal_code": "69000",
"google_url": null,
"doctolib_url": null,
"website_url": "https://…",
"instagram_url": null,
"facebook_url": null,
"tiktok_url": null,
"linkedin_url": null,
"specialities": [{ "id": "…", "name": "Cardiologie", "slug": "cardiologie" }],
"doctors": [{ "id": "…", "slug": "camille-dupont", "full_name": "Dr. Camille Dupont" }]
} }POST /api/v1/doctors et POST /api/v1/clinicsacceptent aussi un tableau d'objets pour créer plusieurs fiches en une seule requête (20 maximum). Chaque élément est traité indépendamment, dans l'ordre du tableau : une erreur sur l'un n'empêche pas la création des autres.Corps de la requête
[
{ "full_name": "Dr. Camille Dupont", "city": "Lyon" },
{ "full_name": "Dr. Nom Invalide" },
{ "full_name": "Dr. Alex Martin", "city": "Paris" }
]Réponse — 201 si tout a réussi, 207 si certains éléments ont échoué
{
"data": [ { "id": "…", "full_name": "Dr. Camille Dupont", … }, { "id": "…", "full_name": "Dr. Alex Martin", … } ],
"errors": [ { "index": 1, "error": "..." } ]
}Codes d'erreur
| Code | Signification | Quand |
|---|---|---|
| 400 | Requête invalide | Le corps de la requête n'est pas un JSON valide. |
| 401 | Non authentifié | En-tête Authorization absent, jeton invalide, ou accès API non activé pour ce compte (has_api_access). |
| 404 | Introuvable | L'identifiant demandé n'existe pas, ou existe mais appartient à un autre compte — les deux cas répondent 404, jamais 403, pour ne pas révéler l'existence de fiches d'autrui. |
| 422 | Champs invalides | Échec de validation (champ requis manquant, URL mal formée, speciality_ids n'est pas un tableau d'identifiants, tableau de création groupée vide ou trop long…). |
| 207 | Succès partiel | Création groupée : certains éléments du tableau ont échoué, d'autres ont réussi. |
| 500 | Erreur serveur | Échec inattendu côté base de données. |
Toute réponse d'erreur a la forme { "error": "message en français" }.
- L'accès à l'API doit être activé pour votre compte (visible dans
Tableau de bord → API) ; sans ça, le jeton existe mais n'est accepté par aucun endpoint. - Seules vos propres fiches sont accessibles ; un identifiant qui ne vous appartient pas répond
404. PATCHremplace tous les champs envoyés, comme le formulaire du tableau de bord — ce n'est pas une mise à jour partielle des champs omis (horsspeciality_ids, qui n'est modifié que si vous le fournissez).- Création groupée limitée à 20 éléments par requête.
- Pas encore d'endpoint pour lister les spécialités disponibles ni pour en créer via l'API — récupérez leurs identifiants depuis le tableau de bord.
- Pas encore de limite de débit (rate limiting).
- Les statistiques de vues et de clics ne sont pas encore exposées par l'API — consultez-les depuis le tableau de bord.