L'API OKLÉON
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.
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_…
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"
}
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
- Le statut d'un dossier se déduit de son état — un devis reçu, un accord, un bon de commande envoyé. Il ne se pose pas. Vouloir l'écrire est du temps perdu.
- Le journal est en lecture seule, et ses dates ne sont pas des dates. Il est
stocké tel qu'il s'affiche à l'écran (
16/09/2026 14:22), pas en ISO 8601. Nous le renvoyons tel quel plutôt que de feindre une précision qu'il n'a pas. - Les identifiants
bien_…,loc_…,own_…,art_…sont des chaînes. Les convertir en entier les met silencieusement à zéro. - Une valeur vide ressort à
null, jamais à"": vous distinguez ainsi « jamais renseigné » de « effacé ». - Lisez
/espaceen premier. Toutes les agences n'ont ni les mêmes options ni les mêmes types de sinistre. Coder en dur ce que vous voyez chez votre premier client, c'est casser chez le deuxième. - La corbeille n'est pas servie par défaut. Un miroir qui l'avalerait rouvrirait chez vous des dossiers que l'agence a jetés.
- Gardez les
ETag. Renvoyés enIf-None-Match, ils vous évitent de retélécharger une fiche inchangée, et renvoyés enIf-Match, ils vous empêchent d'écraser une correction faite à l'écran (voir l'écriture).
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"
}
}
| HTTP | Code | Ce que ça veut dire |
|---|---|---|
| 401 | cle_absente | Clé absente ou invalide. Si vous êtes sûr d'elle, essayez X-Api-Key. |
| 403 | portee_manquante | La clé existe mais n'a pas cette portée. C'est l'agence qui les choisit. |
| 403 | ecriture_fermee | L'écriture n'est pas ouverte sur cet espace. |
| 404 | introuvable | Cette fiche n'existe pas — ou plus. |
| 404 | route_inconnue | Adresse inexistante. Vérifiez la référence. |
| 404 | collection_inactive | Cette agence n'a pas cette option (le module Permis de louer, par exemple). |
| 400 | idempotency_key_requise | Une création sans en-tête Idempotency-Key. |
| 400 | corps_invalide | Le corps n'est pas un objet JSON (une fiche par appel). |
| 405 | methode_non_autorisee | Cette méthode n'existe pas sur cette route. |
| 405 | suppression_non_disponible | L'API ne supprime pas. Une sortie de locataire se pose (date_sortie). |
| 409 | external_ref_existante | Cette référence désigne déjà une fiche (error.id). Utilisez PUT …/ext:. |
| 409 | occupation_en_cours | Le logement a déjà un occupant en cours : posez d'abord sa sortie. |
| 409 | requete_en_cours | Une requête avec la même Idempotency-Key est en cours. Réessayez dans un instant. |
| 412 | version_perimee | La fiche a changé depuis votre lecture. Relisez-la, réappliquez (error.etag_actuel). |
| 422 | parametre_invalide | Un paramètre n'a pas la forme attendue — souvent une date mal formée. |
| 422 | validation | Données refusées. error.erreurs liste tous les champs en faute d'un coup. |
| 422 | idempotency_key_reutilisee | Cette clé d'idempotence a servi pour une autre requête. |
| 428 | if_match_requis | Modifier une fiche exige l'en-tête If-Match. |
| 429 | quota_minute · quota_jour | Trop d'appels. Voyez les quotas. |
| 500 | erreur_serveur | Une panne chez nous. Réessayez, puis signalez-la-nous. |
| 503 | collection_indisponible | Cette 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 :
- Un webhook arrive → relisez seulement les fiches qu'il annonce
(
GET /biens/{id}…), une par une. Une fiche annoncéesupprime, ou qui répond 404, se retire de votre copie. - 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. - 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 faire | Appels par jour quand rien ne change | Délai |
|---|---|---|
Interroger /changements toutes les minutes | 1 440 | ≤ 1 min |
| Webhook + relecture ciblée + filet horaire | 24 | 1 à 2 min |
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
Ce qu'il faut prévoir
- Répondez 2xx en moins de 10 secondes, puis traitez. Les redirections ne sont pas suivies.
- Au moins une fois : un envoi peut arriver deux fois (un délai dépassé alors
que vous aviez reçu). Gardez les
iddéjà traités. - Nouveaux essais après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h et 24 h. Au-delà
(environ 45 heures), l'envoi est abandonné et l'adresse coupée : rattrapez alors par
/changements?since=depuis le dernierserver_timereçu. - Pas de temps réel : les envois partent au passage du planificateur, comptez
quelques minutes. L'ordre d'arrivée n'est pas garanti : fiez-vous à
at. - L'agence peut envoyer un essai depuis son écran :
evenementvaut alorsessaietdataest vide. Répondez 2xx. - Adresse en
https://sur le port 443, avec un nom de domaine public.
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
- Liste blanche stricte : un champ inconnu est refusé, pas ignoré. Toutes les
fautes d'un corps reviennent d'un coup (
422 validation,error.erreurs). - Écrivez
nometprenomséparés. OKLÉON composename(« Prénom Nom ») ; la lecture renvoie aussinomseul. - Pas de suppression. Une sortie de locataire se pose (
date_sortie). Un logement n'a qu'un occupant en cours : posez la sortie du précédent avant l'entrée du suivant. - Pas d'écho : ce que votre clé écrit ne vous revient pas en webhook.
- L'agence garde la main : tout ce que votre clé écrit apparaît dans son écran et peut être annulé pendant 90 jours. Une fiche qu'elle a retouchée depuis n'est jamais écrasée par une annulation.
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.