Connecter vos outils (API)
Tout ce que vos utilisateurs font à l'écran, un programme peut le faire à leur place : lire des fiches, en créer, en modifier, en supprimer. C'est ce que permet l'API de GesHoms, à l'adresse /api/v1/.
Les usages les plus courants : alimenter un objet depuis votre site web (un formulaire de contact qui crée une fiche), synchroniser avec un logiciel existant (ERP, caisse, comptabilité), brancher un outil d'automatisation (Make, n8n, Zapier), ou sortir vos données vers un tableau de bord.
Pour un import ponctuel depuis Excel ou CSV, l'API est un détour inutile. Utilisez l'import de fichier, qui devine la correspondance des colonnes et annonce les erreurs avant d'écrire.
Le principe : un jeton porte des rôles
L'API n'a pas de droits à elle. Un jeton reçoit un ou plusieurs rôles, exactement les mêmes que ceux de vos utilisateurs internes (Droits d'accès), et il peut faire ce que ces rôles autorisent : ni plus, ni moins.
La conséquence est importante : un jeton sans rôle ne voit rien. Il n'y a pas de mode « accès complet ». Pour donner accès à un objet, il faut un rôle qui l'accorde, objet par objet et action par action.
Ce choix a trois avantages :
- vous réglez les droits d'un programme au même endroit et de la même façon que ceux d'une personne ;
- un champ masqué à un rôle l'est aussi pour l'API, y compris à travers un champ calculé qui s'appuierait dessus ;
- un périmètre par enregistrement (« seulement les fiches de tel magasin ») s'applique aussi aux appels du programme.
1. Préparer un rôle dédié
N'accordez pas à un outil tiers le rôle de vos commerciaux. Créez-lui le sien, avec le strict nécessaire.
- Administration → onglet Utilisateurs & droits → Rôles → Nouveau rôle, par exemple
Intégration site web. - Ouvrir l'objet concerné dans le Studio → onglet Permissions, et cocher sur la ligne de ce rôle les seules actions utiles. Pour un formulaire de contact qui dépose des demandes :
Créer, rien d'autre. - Toujours dans les permissions, restreindre les champs si l'outil n'a pas à tout voir.
2. Créer le jeton
Administration → onglet Applications → section 🔌 API (outils tiers) → Nouveau jeton.
| Réglage | À quoi il sert |
|---|---|
| Nom | Reconnaître l'outil dans la liste et dans le journal. Écrivez à quoi il sert, pas « test ». |
| Rôles accordés | Les droits du jeton. Au moins un, sinon la création est refusée. |
| Limite d'appels / min | Garde-fou contre un script emballé. 0 signifie illimité. |
| Expiration | Date au-delà de laquelle le jeton cesse de fonctionner. Recommandée pour un prestataire. |
| IP autorisées | Liste d'adresses, séparées par des virgules. Vide signifie « depuis n'importe où ». |
À la création, GesHoms affiche le jeton en clair, une seule fois. Il n'est pas stocké en clair et personne ne peut le relire ensuite, pas même vous. Copiez-le immédiatement dans le coffre de mots de passe de l'outil. Perdu, il ne se récupère pas : on en crée un autre et on révoque l'ancien.
Dans la liste, chaque jeton affiche son état (Actif, Expiré, Révoqué), son préfixe public, le nombre de rôles et la date de dernier usage. Le bouton Révoquer coupe l'accès immédiatement, sans rien supprimer de vos données.
3. Appeler l'API
Toutes les requêtes portent le jeton dans un en-tête :
Authorization: Bearer <votre-jeton>
Quatre routes suffisent à tout faire.
| Route | Méthode | Effet |
|---|---|---|
/api/v1/health/ | GET | Ce que le jeton peut faire : objets accessibles, actions autorisées, expiration |
/api/v1/openapi.json | GET | La description technique de votre API, limitée aux droits du jeton |
/api/v1/data/<objet>/ | GET | Lister les fiches |
/api/v1/data/<objet>/ | POST | Créer une fiche |
/api/v1/data/<objet>/<id>/ | GET | Lire une fiche |
/api/v1/data/<objet>/<id>/ | PATCH | Modifier une fiche (seuls les champs envoyés changent) |
/api/v1/data/<objet>/<id>/ | DELETE | Mettre une fiche à la corbeille |
<objet> est le code de l'objet, pas son libellé : client, commande, ligne_commande. Vous le lisez dans le Studio, sous le nom de l'objet.
Commencer par se présenter
curl -H "Authorization: Bearer ght_xxx…" \
https://votre-domaine/api/v1/health/
{
"ok": true,
"token": "Intégration site web",
"expires_at": null,
"rate_limit_per_min": 60,
"objects": [
{"code": "client", "name": "Client", "actions": ["create", "read"]}
]
}
C'est le premier appel à faire quand quelque chose ne marche pas : il répond à la question « ce jeton a-t-il vraiment le droit que je crois ? ».
Créer une fiche
curl -X POST https://votre-domaine/api/v1/data/client/ \
-H "Authorization: Bearer ght_xxx…" \
-H "Content-Type: application/json" \
-d '{"nom": "Boulangerie Martin", "email": "contact@martin.fr", "ville": "Nantes"}'
La réponse est la fiche créée, avec son id et ses valeurs telles que GesHoms les a enregistrées :
{"id": 42, "created_at": "2026-08-20T08:21:12+00:00", "nom": "Boulangerie Martin",
"email": "contact@martin.fr", "ville": "Nantes", "magasin": null}
Lister, chercher, paginer
curl -H "Authorization: Bearer ght_xxx…" \
"https://votre-domaine/api/v1/data/client/?search=martin&limit=50&offset=0"
{"count": 137, "results": [ … ]}
| Paramètre | Rôle | Défaut |
|---|---|---|
search | Recherche libre sur les champs texte lisibles par le jeton, champs sensibles exclus | vide |
filters | Filtres précis, en JSON (section suivante) | aucun |
order_by | Champ de tri | les plus récentes d'abord |
order_dir | asc ou desc | asc |
limit | Nombre de fiches par page, plafonné à 200 | 50 |
offset | Rang de départ, pour parcourir page par page | 0 |
count est le total qui correspond à la recherche et aux filtres, pas le nombre de lignes renvoyées : c'est lui qui vous dit quand vous arrêter de paginer.
Filtrer et trier
search cherche un mot partout ; filters pose des conditions précises. Le paramètre attend du JSON, une liste de triplets ["champ", "opérateur", "valeur"] :
curl -G https://votre-domaine/api/v1/data/devis/ \
-H "Authorization: Bearer ght_xxx…" \
--data-urlencode 'filters=[["statut","eq","envoye"],["total_ht","gt",1000]]' \
--data-urlencode 'order_by=date' --data-urlencode 'order_dir=desc'
Les conditions se cumulent : toutes doivent être vraies. La forme longue [{"field": "statut", "op": "eq", "value": "envoye"}] est acceptée aussi.
| Type de champ | Opérateurs |
|---|---|
| Texte, texte long | eq, neq, contains, starts |
| Nombre, décimal | eq, neq, gt, gte, lt, lte, between |
| Date, date et heure | on, before, after, between, in_next, in_last |
| Liste de choix | eq, neq, in, not_in |
| Relation | eq, in |
| Oui / Non | is_true, is_false |
| Tous | empty, not_empty |
Colonnes système id, created_at, updated_at | comme un nombre ou une date |
between attend deux valeurs (["total_ht","between",[1000,5000]]). in_last et in_next comptent des jours (["date","in_last",30]), ce qui évite de calculer une date pivot à chaque appel.
updated_at se filtre comme une date. Une synchronisation qui tourne chaque nuit demande donc [["updated_at","in_last",1]] plutôt que de relire la table entière, et le tri order_by=updated_at lui rend les fiches dans l'ordre où elles ont bougé.
Un champ inconnu, un opérateur qui ne va pas avec le type, un JSON mal formé : la réponse est un 400 qui dit lequel. C'est délibéré. Ignorer un critère incompris renverrait toute la table à un programme persuadé d'avoir filtré, et le bug ne se verrait que le jour où quelqu'un compte les lignes.
Un champ calculé (formule, cumul) est évalué à la lecture, après la requête : le filtrer fausserait count et la pagination. Un champ chiffré ne peut pas être comparé en base. Dans les deux cas, la réponse est un 400 explicite plutôt qu'un résultat approximatif. Enfin, un champ que le jeton n'a pas le droit de lire ne peut pas non plus servir de critère : sinon l'ordre du classement suffirait à en deviner les valeurs.
La liste exacte des champs triables de votre objet est dans openapi.json, sur le paramètre order_by.
Modifier, supprimer
curl -X PATCH https://votre-domaine/api/v1/data/client/42/ \
-H "Authorization: Bearer ght_xxx…" -H "Content-Type: application/json" \
-d '{"ville": "Rennes"}'
curl -X DELETE https://votre-domaine/api/v1/data/client/42/ \
-H "Authorization: Bearer ght_xxx…"
PATCH ne touche que les champs envoyés, les autres gardent leur valeur. DELETE met la fiche à la corbeille, d'où un administrateur peut la restaurer.
En Python
import requests
API = "https://votre-domaine/api/v1"
H = {"Authorization": "Bearer ght_xxx…"}
r = requests.post(f"{API}/data/client/", headers=H, timeout=10,
json={"nom": "Boulangerie Martin", "email": "contact@martin.fr"})
r.raise_for_status()
print("fiche créée :", r.json()["id"])
Quelle valeur envoyer pour quel champ
Le nom de la clé JSON est toujours le code du champ, celui qui sert aussi dans les formules.
| Type de champ | Ce que l'API attend | Exemple |
|---|---|---|
| Texte, texte long | une chaîne | "Boulangerie Martin" |
| Nombre entier | un entier | 12 |
| Nombre décimal | un nombre | 1250.50 (point décimal, pas de virgule ni d'espace) |
| Oui / Non | un booléen | true |
| Date | AAAA-MM-JJ | "2026-08-20" |
| Date et heure | date et heure ISO | "2026-08-20T14:30:00" |
| Liste de choix | le code de l'option, pas son libellé | "envoye" |
| Relation vers un objet | l'identifiant de la fiche liée | 7 |
Une valeur vide s'envoie avec null. Un champ absent du corps est simplement laissé tel quel en modification, et laissé vide à la création.
Un champ calculé (formule) ou un cumul apparaît dans les réponses, mais l'envoyer en écriture n'a aucun effet : sa valeur découle des données, elle ne se pose pas. Voir Relations & calculs.
Un champ marqué sensible est chiffré dans la base et restitué en clair aux appels autorisés, sans que vous ayez rien à faire.
Comprendre les réponses
| Code | Signification | Quoi faire |
|---|---|---|
200 | Lecture ou modification réussie | |
201 | Fiche créée | l'id est dans la réponse |
204 | Fiche supprimée | rien à lire, le corps est vide |
400 | Donnée refusée | le message dit quel champ et pourquoi (obligatoire manquant, valeur invalide) |
401 | Jeton absent, invalide, expiré ou révoqué | vérifier l'en-tête, puis l'état du jeton dans l'écran Applications |
403 | Action non autorisée sur cet objet, ou IP non autorisée | ajouter l'action au rôle, ou l'adresse à la liste blanche |
404 | Objet ou fiche introuvable | voir l'encadré ci-dessous |
429 | Trop d'appels par minute | ralentir, ou relever la limite du jeton |
Si le jeton n'a aucun rôle sur un objet, l'API répond 404, pas 403. C'est volontaire : un 403 révélerait qu'un objet nommé salaire existe chez vous. Devant un 404 inattendu, appelez /api/v1/health/ : si l'objet n'y figure pas, le problème est un rôle manquant, pas une adresse erronée.
Explorer sans écrire une ligne de code
Deux façons de voir votre API telle qu'elle est, avec vos objets et vos champs :
- l'explorateur d'API de cette documentation : collez l'adresse de votre GesHoms et un jeton, il affiche les objets accessibles, les champs de chacun, teste les lectures et vous donne la commande
curltoute faite pour les écritures ; GET /api/v1/openapi.json, qui renvoie une description OpenAPI 3 de votre API, limitée à ce que le jeton peut faire. Ce fichier s'importe directement dans Postman, Insomnia ou Swagger, et sert de source à la plupart des générateurs de code.
Un programme sur un serveur appelle l'API sans contrainte particulière. Une page web, elle, est soumise aux règles du navigateur : l'origine qui appelle doit être déclarée côté serveur (variable DJANGO_CORS_ALLOWED_ORIGINS). Si l'explorateur affiche « appel bloqué par le navigateur », c'est cette déclaration qui manque, pas votre jeton. Votre administrateur technique trouvera le détail dans DEPLOY.md.
Bonnes pratiques
- Un jeton par outil. Révoquer celui de la boutique en ligne ne doit pas couper la comptabilité, et le journal doit dire qui a fait quoi.
- Le rôle le plus étroit possible. Un formulaire de contact n'a besoin que de
Créersur un seul objet. Il n'a pas besoin de lire les clients existants. - Une expiration sur les accès prestataires. Un jeton sans date de fin survit à la fin de la mission.
- Une limite d'appels. Un script en boucle est vite arrivé, et la limite est ce qui protège vos utilisateurs de son emballement.
- Vérifier avant d'écrire en masse. Testez sur une fiche, lisez la réponse, puis lancez le traitement complet.
- Ne mettez jamais un jeton dans une page web publique. Ce qui est envoyé au navigateur est lisible par tout le monde. Un jeton vit sur un serveur, dans une variable d'environnement.
Ce que l'API ne fait pas encore
Autant le savoir avant de concevoir votre intégration :
- pas de webhook sortant : GesHoms ne vient pas frapper à la porte de votre outil quand une fiche change. C'est votre programme qui interroge l'API ;
- pas de création d'objets ni de champs par l'API : elle porte les données, pas la structure. La structure se construit dans le Studio.
Suite
L'atelier 6 construit une intégration complète, du rôle jusqu'à la révocation, avec les erreurs volontaires qui apprennent le plus.