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.

Authentification
Chaque compte dispose d'un jeton personnel, disponible dans Tableau de bord → API. Passez-le dans l'en-tête 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

Doctor
Renvoyé par les endpoints /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.
ChampTypeDescription
idstring (uuid)Identifiant, généré à la création.
slugstringSegment d'URL public (/doctors/:slug). Généré une fois à la création à partir de full_name, jamais recalculé.
full_namestringNom complet. Obligatoire à la création (2 à 200 caractères).
biostring | nullPrésentation libre (4000 caractères max).
addressstring | nullAdresse (200 caractères max).
citystring | nullVille (100 caractères max).
postal_codestring | nullCode postal (20 caractères max).
google_urlstring | nullFiche Google
doctolib_urlstring | nullProfil Doctolib
website_urlstring | nullSite web personnel
instagram_urlstring | nullInstagram
facebook_urlstring | nullFacebook
tiktok_urlstring | nullTikTok
linkedin_urlstring | nullLinkedIn
specialitiesArray<{ id, name, slug }>Spécialités associées. En écriture, fournissez speciality_ids (voir ci-dessous) plutôt que ce tableau.
clinicsArray<{ 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 ».
Clinic
Renvoyé par les endpoints /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.
ChampTypeDescription
idstring (uuid)Identifiant, généré à la création.
slugstringSegment d'URL public (/clinics/:slug). Généré une fois à la création à partir de name, jamais recalculé.
namestringNom du cabinet. Obligatoire à la création (2 à 200 caractères).
biostring | nullPrésentation libre (4000 caractères max).
addressstring | nullAdresse (200 caractères max).
citystring | nullVille (100 caractères max).
postal_codestring | nullCode postal (20 caractères max).
google_urlstring | nullFiche Google
doctolib_urlstring | nullProfil Doctolib
website_urlstring | nullSite web personnel
instagram_urlstring | nullInstagram
facebook_urlstring | nullFacebook
tiktok_urlstring | nullTikTok
linkedin_urlstring | nullLinkedIn
specialitiesArray<{ id, name, slug }>Spécialités associées. En écriture, fournissez speciality_ids (voir ci-dessous) plutôt que ce tableau.
doctorsArray<{ id, slug, full_name }>Médecins associés — lecture seule sur cet objet. Gérez l'association via les endpoints ci-dessous.
Champs propres à l'écriture
Acceptés en 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).
ChampTypeDescription
speciality_idsstring[] (uuids), facultatifSur 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

GET/api/v1/me
Informations sur le compte propriétaire du jeton.
{
  "data": {
    "id": "5b1e...-uuid",
    "email": "[email protected]",
    "full_name": "Dr. Camille Dupont"
  }
}

Médecins

GET/api/v1/doctors
Liste des pages médecin que vous possédez.
{
  "data": [
    {
      "id": "3f2a...-uuid",
      "slug": "camille-dupont",
      "full_name": "Dr. Camille Dupont",
      "city": "Lyon"
    }
  ]
}
POST/api/v1/doctors
Crée une nouvelle page médecin. Le slug public est généré automatiquement à partir de full_name. Accepte aussi un tableau pour créer plusieurs médecins d'un coup (voir « Création groupée » plus bas).

Corps 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" }]
  } }
GET/api/v1/doctors/:id
Détail d'une de vos pages médecin (uniquement les vôtres).
{ "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" }]
  } }
PATCH/api/v1/doctors/:id
Remplace les champs d'une page médecin (comme le formulaire du tableau de bord : envoyez tous les champs, pas seulement ceux qui changent).

Corps 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" }]
  } }
DELETE/api/v1/doctors/:id
Supprime une page médecin. Réponse 204 sans corps si la suppression réussit.
HTTP/1.1 204 No Content

Cabinets

GET/api/v1/clinics
Liste des fiches cabinet que vous possédez.
{
  "data": [
    {
      "id": "9c4d...-uuid",
      "slug": "cabinet-du-parc",
      "name": "Cabinet du Parc",
      "city": "Lyon"
    }
  ]
}
POST/api/v1/clinics
Crée une nouvelle fiche cabinet. Le slug public est généré automatiquement à partir de name. Accepte aussi un tableau pour créer plusieurs cabinets d'un coup (voir « Création groupée » plus bas).

Corps 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" }]
  } }
GET/api/v1/clinics/:id
Détail d'une de vos fiches cabinet (uniquement les vôtres).
{ "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" }]
  } }
PATCH/api/v1/clinics/:id
Remplace les champs d'une fiche cabinet (envoyez tous les champs, comme le formulaire du tableau de bord).

Corps 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" }]
  } }
DELETE/api/v1/clinics/:id
Supprime une fiche cabinet. Réponse 204 sans corps si la suppression réussit.
HTTP/1.1 204 No Content

Association 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).

POST/api/v1/clinics/:id/doctors
Ajoute un médecin (que vous possédez déjà) à ce cabinet. doctor_id doit être l'identifiant d'une de vos pages médecin existantes — créez-la d'abord avec POST /api/v1/doctors si besoin. Idempotent : lier deux fois le même médecin ne crée pas de doublon.

Corps 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" }]
  } }
DELETE/api/v1/clinics/:id/doctors/:doctorId
Retire un médecin de ce cabinet (sans supprimer ni le médecin ni le cabinet). Réponse 204 sans corps si la suppression réussit.
HTTP/1.1 204 No Content
Exemple : ajouter un médecin existant à un cabinet
Le médecin et le cabinet doivent déjà exister et vous appartenir tous les deux. Créez-les d'abord séparément si besoin, puis liez-les.
curl -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" }]
  } }
Création groupée
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

CodeSignificationQuand
400Requête invalideLe corps de la requête n'est pas un JSON valide.
401Non authentifiéEn-tête Authorization absent, jeton invalide, ou accès API non activé pour ce compte (has_api_access).
404IntrouvableL'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.
422Champs 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…).
207Succès partielCréation groupée : certains éléments du tableau ont échoué, d'autres ont réussi.
500Erreur serveurÉchec inattendu côté base de données.

Toute réponse d'erreur a la forme { "error": "message en français" }.

Limites actuelles
  • 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 (hors speciality_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.