Guichet v1 · contrat stable

Le formulaire du site parle au cabinet, et à personne d'autre.

Une clé, un cabinet. Le site dépose une demande signée ; il ne relit jamais un patient, jamais une tournée, jamais une donnée de santé. Cette page est la notice complète — elle est produite par le code qui répond, et suit donc chaque évolution.

Fenêtre anti-rejeu

300 s

Assez pour un relais lent, trop peu pour un rejeu utile.

Algorithme

HMAC-SHA256 (hex)

Comparaison en temps constant : la longueur du préfixe correct ne fuit pas.

Corps signé

le corps brut

On vérifie avant de parser : une signature sur du JSON re-sérialisé ne prouve rien.

Horodatage

secondes ou millisecondes

Les deux dialectes sont acceptés, à vérifications strictement identiques.

Taille maximale du corps

64 000 octets

Un formulaire de contact n'a pas besoin de plus, et une pièce jointe passe ailleurs.

Les portes

Chaque porte demande la portée indiquée. Une clé ne peut que ce que le cabinet lui a accordé.

AppelPortéeCe qu'elle fait
POST
/api/public/v1/intake
succès 201 · conflit 409
intake
signe le corps
Déposer une arrivée. Le site du cabinet transmet une demande signée. La référence sert de clé d'idempotence : deux envois identiques ne créent qu'une demande.
GET
/api/public/v1/intake/{tracking_token}/status
intake
signe la cible
Relire l'état d'une arrivée. Rend le statut normalisé et l'horodatage. Aucune donnée de santé, aucun patient, aucune tournée n'en sort jamais.
GET
/api/public/v1/intake/{tracking_token}/statut
intake
signe la cible
Relire l'état (orthographe française). Exactement la même réponse que `/status`. Servie à l'identique, sans date de fin : un client écrit hier fonctionne demain.
GET
/api/public/v1/availability
availability
signe la cible
Voir les fenêtres ouvertes. Rend des fenêtres (matin, milieu de journée, soir) et leur tension — jamais l'agenda, jamais un nom, jamais un patient.
POST
/api/public/v1/holds
succès 201 · conflit 409
holds
signe le corps
Retenir un créneau. Pose une retenue temporaire. Elle n'est pas un rendez-vous : le cabinet confirme, ou la retenue expire d'elle-même.
GET
/api/public/v1/holds/{hold_token}
holds
signe la cible
Relire une retenue. Dit si la retenue tient encore, a été confirmée, libérée ou annulée.
GET
/api/public/v1/contrat
lecture
signe la cible
Lire le contrat, par la machine. Le même contenu que cette notice, en JSON : schémas des canaux, en-têtes, fenêtre anti-rejeu, codes d'erreur.
PAGE
/ma-demande/{tracking_token}
intake
aucune signature
La page de suivi de la personne. L'adresse à donner au visiteur après son dépôt. Elle s'ouvre sans compte et ne montre rien d'autre que l'avancement.

Signer, puis vérifier

La matière signée est l'horodatage, un point, puis le corps brut. Une signature calculée sur du JSON re-sérialisé ne prouve rien : c'est l'erreur la plus fréquente, et ce banc l'écarte en trente secondes.

Banc de signature

Tout se calcule ici, dans votre navigateur. Le secret n'est ni envoyé, ni journalisé, ni conservé — quittez la page et il disparaît.

Matière donnée à HMAC-SHA256

1788106854.{
  "accountId": "votre-account-id",
  "channel": "soins_domicile",
  "reference": "site-2026-000418",
  "submitted_at": "2026-08-26T09:12:00.000Z",
  "consent": true,
  "payload": {
    "nom": "Dupont",
    "prenom": "Camille",
    "telephone": "+33 6 12 34 56 78",
    "commune": "Grenoble",
    "message": "Pansement à refaire, sortie d'hospitalisation."
  }
}

Les échos sortants

Le cabinet peut déclarer une adresse HTTPS : chaque changement d'état y est frappé, signé de la même façon, avec reprise progressive en cas de panne.

intake.received

Demande reçue

intake.reviewing

Demande lue par le cabinet

intake.scheduled

Passage planifié

intake.completed

Passage effectué

intake.closed

Demande clôturée

hold.created

Créneau retenu

hold.confirmed

Créneau confirmé

hold.released

Créneau libéré

Le vocabulaire

Listes fermées : un client peut les coder en dur sans risque. On ajoute, on n'ôte pas.

status (à tester)

receivedreviewingscheduledcompletedclosed

state (nuance interne)

recueluecreneau_retenuplanifieecloturee

slot_windows

morningmiddayevening

window_capacity

opentightclosed

hold_statuses

heldconfirmedexpiredcancelled

Quand ça refuse

Chaque erreur porte trois champs : error (le mot normalisé, à tester), code (notre code interne, journalisable) et message (la phrase française).

errorcodeCe qu'il faut corriger
unauthorizedsignature_invalideRecalculez la signature sur le corps brut, octet pour octet, avant toute re-sérialisation.
unauthorizedcle_inconnueL'identifiant de clé n'est pas reconnu : vérifiez l'en-tête `key-id`.
forbiddencle_revoqueeCette clé a été retirée par le cabinet. Demandez-lui d'en émettre une nouvelle.
forbiddenportee_refuseeLa clé n'a pas cette portée. Le cabinet peut l'accorder depuis son atelier.
forbiddenorigine_refuseeL'origine du navigateur n'est pas déclarée. Déposez de serveur à serveur, ou faites déclarer l'origine.
channel_closedcanal_fermeLe cabinet a fermé ce canal. Proposez-en un autre, ou retirez le formulaire.
account_mismatchdestinataire_invalideLe `accountId` de l'enveloppe ne correspond pas au compte porteur de la clé.
invalid_jsoncorps_illisibleLe corps n'est pas du JSON valide : cherchez du côté de la sérialisation.
invalid_payloadrequete_invalideUn champ manque ou dépasse sa taille : la réponse nomme lequel.
not_foundroute_inconnueCette adresse n'existe pas. La liste des portes est ci-dessus.
not_foundintrouvableLe jeton ne désigne rien, ou plus rien. Ne le rejouez pas.
conflictcreneau_indisponibleLe créneau était libre, il ne l'est plus. Relisez les fenêtres et reproposez.
conflictreference_connueCette référence a déjà servi : la demande existe, elle n'est pas recréée.
rate_limitedquota_depasseRalentissez : les plafonds par minute et par jour figurent dans le contrat.
unavailableindisponiblePanne passagère de notre côté. Réessayez avec un retrait progressif.