Intégration

L'API OKLÉON

Version 1 · lecture, écriture et webhooks · octobre 2026

Branchez votre logiciel sur l'espace d'une agence : lisez et alimentez son portefeuille de logements, ses locataires et propriétaires, qui habite où, et déclarez-y des sinistres.

Commencez par le bac à sable. demo.okleon.fr contient des données entièrement fictives et des clés préfixées ok_test_. Vous pouvez tout y essayer avant de demander l'accès à l'espace d'une vraie agence.

En deux minutes

# Est-ce que ma clé passe ?
curl -H "X-Api-Key: ok_test_VOTRE_CLE" \
     https://demo.okleon.fr/api/v1/ping

# De quoi est fait cet espace ?
curl -H "X-Api-Key: ok_test_VOTRE_CLE" \
     https://demo.okleon.fr/api/v1/espace

# Tout ce qui a changé depuis hier
curl -H "X-Api-Key: ok_test_VOTRE_CLE" \
     "https://demo.okleon.fr/api/v1/changements?since=2026-09-19T00:00:00%2B02:00"

L'espace est dans l'adresse

Il n'y a pas d'api.okleon.fr. Chaque agence a son propre hôte, et c'est l'hôte qui choisit la base de données. Une clé présentée sur agence-a.okleon.fr ne peut donc rien atteindre chez agence-b.okleon.fr : l'isolation n'est pas une règle que nous appliquons, c'est la structure elle-même.

Concrètement : une clé, un espace. Si vous intégrez plusieurs agences, vous aurez autant de couples (hôte, clé) que d'agences.

S'authentifier

Deux en-têtes acceptés, au choix :

Authorization: Bearer ok_live_…
X-Api-Key: ok_live_…
Si vous recevez un 401 alors que votre clé est bonne, passez à X-Api-Key. Certains hébergements PHP retirent l'en-tête Authorization avant qu'il n'atteigne le script : la clé arrive vide et rien n'a l'air cassé. X-Api-Key ne dépend d'aucun réglage serveur. La route /ping vous dit par quelle voie votre clé est arrivée.

Il n'y a pas de CORS, volontairement. Une clé serveur n'a rien à faire dans le JavaScript d'une page : elle y serait lisible par n'importe qui. Appelez depuis votre serveur.

Se synchroniser

N'interrogez pas les collections une par une : /changements est un flux unique, trié dans le temps, qui couvre toutes les collections et les suppressions.

{
  "data": [
    { "collection": "biens", "id": "bien_000412", "external_ref": "LOT-88",
      "action": "modifie", "at": "2026-09-20T09:14:03+02:00" },
    { "collection": "locataires", "id": "loc_000091", "external_ref": null,
      "action": "supprime", "at": "2026-09-20T09:40:55+02:00" }
  ],
  "next_cursor": null,
  "server_time": "2026-09-20T11:02:18+02:00"
}
Stockez server_time comme point de reprise, jamais l'heure de votre machine. Deux horloges qui divergent d'une minute font sauter une minute de changements — en silence, et vous ne le découvrirez que le jour où une fiche manquera.

⭐ Interroger ce flux en boucle marche, mais coûte cher pour rien. Faites-vous plutôt prévenir par webhook, et gardez /changements comme filet de sécurité, une fois par heure.

La pagination est un curseur (next_cursor → ?cursor=), jamais un décalage numérique : pendant une synchronisation, une fiche modifiée décalerait tout ce qui suit et vous en sauteriez.

Ce qu'il faut savoir avant de coder

Quotas

Par clé : 120 appels par minute et 20 000 par jour. La taille de page vaut 100 par défaut et 500 au maximum (?limit=).

L'écriture a son propre plafond, en plus : 60 écritures par minute et 5 000 par jour (quota_ecriture_minute, quota_ecriture_jour).

Au-delà, la réponse est un 429 accompagné d'un en-tête Retry-After qui indique le nombre de secondes à attendre. Respectez-le plutôt que de réessayer en boucle.

Une synchronisation bien faite consomme très peu : un appel à /changements, puis seulement les fiches qui ont bougé.

Les erreurs

Toutes les erreurs ont la même forme. Le code est un contrat : il ne change jamais. Branchez-vous dessus, pas sur le message, qui peut être reformulé.

{
  "error": {
    "code": "portee_manquante",
    "message": "Cette clé n'a pas la portée « sinistres:read ».",
    "documentation": "https://okleon.fr/developpeurs/#erreurs"
  }
}
HTTPCodeCe que ça veut dire
401cle_absenteClé absente ou invalide. Si vous êtes sûr d'elle, essayez X-Api-Key.
403portee_manquanteLa clé existe mais n'a pas cette portée. C'est l'agence qui les choisit.
403ecriture_fermeeL'écriture n'est pas ouverte sur cet espace.
404introuvableCette fiche n'existe pas — ou plus.
404route_inconnueAdresse inexistante. Vérifiez la référence.
404collection_inactiveCette agence n'a pas cette option (le module Permis de louer, par exemple).
400idempotency_key_requiseUne création sans en-tête Idempotency-Key.
400corps_invalideLe corps n'est pas un objet JSON (une fiche par appel).
405methode_non_autoriseeCette méthode n'existe pas sur cette route.
405suppression_non_disponibleL'API ne supprime pas. Une sortie de locataire se pose (date_sortie).
409external_ref_existanteCette référence désigne déjà une fiche (error.id). Utilisez PUT …/ext:.
409occupation_en_coursLe logement a déjà un occupant en cours : posez d'abord sa sortie.
409requete_en_coursUne requête avec la même Idempotency-Key est en cours. Réessayez dans un instant.
412version_perimeeLa fiche a changé depuis votre lecture. Relisez-la, réappliquez (error.etag_actuel).
422parametre_invalideUn paramètre n'a pas la forme attendue — souvent une date mal formée.
422validationDonnées refusées. error.erreurs liste tous les champs en faute d'un coup.
422idempotency_key_reutiliseeCette clé d'idempotence a servi pour une autre requête.
428if_match_requisModifier une fiche exige l'en-tête If-Match.
429quota_minute · quota_jourTrop d'appels. Voyez les quotas.
500erreur_serveurUne panne chez nous. Réessayez, puis signalez-la-nous.
503collection_indisponibleCette collection est momentanément illisible sur cet espace.

Être prévenu : les webhooks

Plutôt que d'interroger /changements en boucle, faites-vous prévenir. L'agence déclare votre adresse dans Administration → API → Webhooks et l'associe à votre clé : vous recevez alors en POST les changements que cette clé peut lire, sous la même forme que le flux.

POST https://votre-logiciel.fr/okleon/webhook
X-Okleon-Evenement: changements
X-Okleon-Envoi: whe_1842
X-Okleon-Signature: t=1790000000,v1=5f2c9a…

{
  "id": "whe_1842",
  "evenement": "changements",
  "espace": "demo.okleon.fr",
  "server_time": "2026-09-22T17:05:00+02:00",
  "data": [
    { "collection": "biens", "id": "bien_000412", "external_ref": "LOT-88",
      "action": "modifie", "at": "2026-09-22T17:03:41+02:00" }
  ]
}

Le message ne contient que des identifiants, jamais de coordonnées : relisez la fiche avec votre clé. Une adresse de réception mal protégée ne laisse ainsi fuir aucune donnée personnelle.

La bonne façon de se tenir à jour

Le webhook est la sonnette, l'API est la porte : on vous prévient, vous venez lire. Combinez les deux ainsi — c'est le moins coûteux pour vous comme pour nous :

  1. Un webhook arrive → relisez seulement les fiches qu'il annonce (GET /biens/{id}…), une par une. Une fiche annoncée supprime, ou qui répond 404, se retire de votre copie.
  2. Un lot de plus de 20 fiches (un import chez l'agence) → une synchronisation par pages (updated_since) coûte moins d'appels que 100 lectures une à une.
  3. Une fois par heure, un passage sur /changements?since= sert de filet de sécurité : il rattrape un webhook perdu pendant une panne chez vous.
Façon de faireAppels par jour quand rien ne changeDélai
Interroger /changements toutes les minutes1 440≤ 1 min
Webhook + relecture ciblée + filet horaire241 à 2 min
N'interrogez pas en boucle quand vous avez un webhook. Ces appels ne vous apportent rien de plus et comptent dans votre quota. Nous le voyons dans nos journaux, et nous vous le signalerons.

L'application de démonstration fait exactement cela, et montre son code : integration.okleon.fr/prevenu.php.

Vérifier la signature

Calculez un HMAC-SHA256 de t + "." + corps brut avec le secret whsec_… remis à la création, comparez-le à v1 en temps constant, et refusez un t de plus de 5 minutes : un envoi intercepté ne peut pas être rejoué.

$corps = file_get_contents('php://input');   // le corps BRUT, avant tout json_decode
preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $_SERVER['HTTP_X_OKLEON_SIGNATURE'] ?? '', $m);
$attendu = hash_hmac('sha256', ($m[1] ?? '') . '.' . $corps, $secret);
if (!$m || abs(time() - (int)$m[1]) > 300 || !hash_equals($attendu, $m[2])) {
    http_response_code(400); exit;
}
http_response_code(200);   // répondre d'abord, traiter ensuite
Vérifiez sur le corps brut. Un JSON décodé puis réencodé ne donne pas les mêmes octets (espaces, accents, ordre) : la signature ne correspondrait plus.

Ce qu'il faut prévoir

L'écriture

L'écriture s'ouvre espace par espace, à la demande de l'agence — elle est ouverte sur le bac à sable. /espace vous le dit (api.ecriture_ouverte) ; fermée, toute écriture répond 403 ecriture_fermee. L'agence choisit aussi, clé par clé, ce que vous pouvez écrire (biens:write, locataires:write…).

Créer — avec une clé d'idempotence

Chaque création porte un en-tête Idempotency-Key (un UUID par création). Si votre appel expire, réessayez avec la même clé : vous recevez la réponse d'origine (Idempotent-Replayed: true) au lieu d'un doublon.

curl -X POST https://demo.okleon.fr/api/v1/proprietaires \
  -H "X-Api-Key: ok_test_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2e7a-…" \
  -d '{"nom": "Dupont", "prenom": "Jeanne", "email": "[email protected]", "external_ref": "PROP-12"}'

La réponse (201) est la fiche, avec son ETag et son adresse (Location). L'identifiant est attribué par OKLÉON ; posez external_ref et vous n'aurez jamais à retenir le nôtre.

Synchroniser — par votre référence

La route d'un logiciel qui fait foi : PUT /{collection}/ext:VOTRE_REF. Si la fiche n'existe pas, elle est créée ; sinon, les champs envoyés sont mis à jour. Rejouable sans doublon.

PUT /api/v1/biens/ext:LOT-1234
If-Match: *
{"owner_id": "ext:PROP-12", "address": "12 rue des Lilas", "postal_code": "69003", "city": "Lyon"}

Un champ qui désigne une autre fiche (owner_id, bien_id, loc_id) accepte notre identifiant ou le vôtre préfixé ext:.

Modifier — sans écraser l'agence

PATCH ne change que les champs envoyés (null efface), et exige If-Match avec l'ETag que vous avez lu. Si quelqu'un a corrigé la fiche à l'écran entre-temps, vous recevez 412 version_perimee : relisez, réappliquez votre modification. If-Match: * écrase sans vérifier — réservez-le au cas où c'est votre logiciel qui fait foi sur ces champs.

Déclarer un sinistre

POST /sinistres crée un dossier comme une déclaration faite par le locataire : le logement (bien_id), le type (type_id, voir /types), la description, et les faits constatés ("reponses": {"coule": "oui"}). L'occupant se déduit du logement, l'urgence se déduit des faits, la référence SGL-… est attribuée, et le gestionnaire est prévenu. status, urgence, ref et journal sont refusés : un dossier ne se modifie pas par l'API.

Les règles

Obtenir une clé

Les clés sont créées par l'agence elle-même, depuis son espace : Administration → Clés d'API. Elle choisit ce que la clé peut lire — logements, locataires, propriétaires, occupations, artisans, dossiers — et, si l'écriture est ouverte, ce qu'elle peut écrire. Elle peut la révoquer à tout moment.

Une clé n'est affichée qu'une seule fois, à sa création. Nous ne pouvons pas la retrouver ensuite, même pour vous : elle n'est pas stockée en clair.

Une clé fait sortir des coordonnées de personnes physiques (locataires, propriétaires) vers votre système. Vous en devenez responsable au sens du RGPD. Tous les appels sont journalisés côté OKLÉON : qui a appelé quoi, quand, avec quel résultat.