本页尚未翻译成您的语言:现以法语显示。
API et webhooks
Deux sens de circulation : une API REST pour lire l'instance depuis l'extérieur, et des webhooks pour que l'instance prévienne l'extérieur quand quelque chose arrive.
API REST#
Versionnée sous /api/v1, et volontairement en lecture seule.
Un jeton circule dans des fichiers de configuration, des variables d'environnement, parfois un dépôt Git. Il ne doit pas pouvoir supprimer un salarié ni émettre une facture.
Authentification#
curl -H "Authorization: Bearer sm_xxxxxxxxxxxxxxxxxxxx" \
https://votre-instance.example/api/v1/annuaire
Pas de cookie, donc pas de session, donc pas de jeton CSRF : l'authentification tient
entièrement dans l'en-tête. Les réponses ne sont jamais mises en cache
(Cache-Control: no-store), les erreurs sont en JSON, et le débit est limité
par API_RATE_LIMIT (120 requêtes par minute et par adresse, par défaut).
Jetons#
- Portées explicites : annuaire, RH, gestion, projets, pilotage. Un jeton ne voit que ce que sa portée autorise.
- Durée de vie bornée, révocables d'un clic.
- Jamais conservés en clair : seule leur empreinte SHA-256 vit en base, avec le préfixe
sm_qui permet de les reconnaître. Le jeton s'affiche une fois, à sa création.
Qui est retiré de l'annuaire n'en sort pas par une autre porte, et le motif d'une absence ne quitte pas l'entreprise. Les mêmes filtres qu'à l'affichage s'appliquent à la source, pas après coup.
Webhooks sortants#
L'instance appelle une adresse que vous fournissez quand un événement se produit :
- facture créée, facture payée ;
- absence approuvée ;
- arrivée, départ ;
- document signé ;
- ticket ouvert ;
- externalisation de sauvegarde en échec.
Chaque envoi est signé#
Un HMAC-SHA256 du corps de la requête, calculé avec le secret du webhook, est transmis dans
l'en-tête x-toutadmin-signature. C'est ce qui distingue un appel venu
d'ici d'un appel forgé. Le secret est chiffré en base et affiché une seule fois.
// Côté récepteur : vérifier avant de faire quoi que ce soit du corps.
const attendu = crypto
.createHmac('sha256', SECRET)
.update(corpsBrut) // le corps brut, pas l'objet re-sérialisé
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(attendu), Buffer.from(recu))) {
return res.status(401).end();
}
Une adresse interne est refusée par défaut#
Faire émettre des requêtes à un serveur vers son propre réseau est une porte dérobée
classique. Les adresses privées et les URL en clair (http://) sont refusées ;
les autoriser demande de cocher une case, sciemment.
Un échec n'est pas silencieux#
- Chaque tentative est journalisée.
- Réessai cinq fois, avec un délai qui double à chaque tentative.
- Après vingt échecs consécutifs, le webhook s'éteint de lui-même.
Mieux vaut un tuyau éteint, qui se voit, qu'un tuyau muet qui fait croire que l'information passe.
Toutadmin 文档 —— 构建于 2026-09-13。 独立站点,与软件本身分离。