API Kaliio : documentation développeurs
Reliez votre site ou vos automatisations à Kaliio : lire vos formations et vos sessions à venir, inscrire des stagiaires, être prévenu à chaque événement. L'API, les webhooks et le formulaire d'inscription sont inclus dans le plan gratuit. Spécification OpenAPI 3.1 : /api/v1/openapi.json. Mode d'emploi pour l'organisme : API et webhooks.
Authentification
Créez une clé dans Paramètres > Intégrations > Clés d'API, en lecture seule ou en lecture et écriture. Elle n'est affichée qu'une fois et ne donne accès qu'aux données de votre organisme. Envoyez-la dans l'en-tête Authorization: Bearer kaliio_mcp_…, depuis votre serveur uniquement : jamais dans une page web ou une application mobile. La même clé ouvre le serveur MCP. Une clé révoquée est refusée immédiatement (401).
Adresse de base : https://kaliio.fr/api/v1. Corps et réponses en JSON (UTF-8), dates au format ISO 8601, identifiants UUID.
Points d'accès
| Méthode | Chemin | Portée | Rôle |
|---|---|---|---|
GET | /formations | lecture | Catalogue (filtres search, published) |
GET | /formations/{id} | lecture | Une formation |
GET | /sessions | lecture | Sessions à venir, places restantes (filtres from, to, status, formation_id) |
GET | /sessions/{id} | lecture | Une session et ses créneaux |
GET | /sessions/{id}/trainees | lecture | Stagiaires inscrits et leur dossier |
POST | /sessions | écriture | Créer une session d'une formation existante |
POST | /sessions/{id}/enrollments | écriture | Inscrire des stagiaires (dossier « À valider » par défaut) |
GET | /clients | lecture | Clients (filtres search, email) |
GET | /clients/{id} | lecture | Un client |
POST | /clients | écriture | Créer un client |
PATCH | /clients/{id} | écriture | Modifier un client (champs omis inchangés) |
L'API ne supprime rien : les suppressions se font dans l'application, et les preuves signées sont conservées 5 ans. Les écritures appliquent les mêmes règles que l'application : un dossier (« À valider » ou validé) d'une session programmée, ou une session programmée, déclenchent les envois automatiques. La réponse porte alors un champ warning si ces envois n'ont pas pu être programmés.
Exemples
Sessions à venir et places restantes
# Votre clé, créée dans Paramètres > Intégrations
export KALIIO_API_KEY="kaliio_mcp_…"
curl https://kaliio.fr/api/v1/sessions?limit=10 \
-H "Authorization: Bearer $KALIIO_API_KEY"{
"data": [
{
"id": "6f1c…",
"formation": { "id": "2b9e…", "title": "Excel avancé" },
"status": "SCHEDULED",
"start_date": "2026-11-11",
"end_date": "2026-11-12",
"location": "Clermont-Ferrand",
"max_participants": 10,
"enrolled_count": 7,
"places_remaining": 3,
"slots": [{ "date": "2026-11-11", "start_time": "09:00", "end_time": "12:30", "type": "MORNING", … }]
}
],
"pagination": { "limit": 10, "offset": 0, "total": 1, "has_more": false }
}Inscrire un stagiaire
Indiquez client_id (client existant) ou client (client à créer). Sans l'un ni l'autre, un client « Particulier » est créé au nom du premier stagiaire. Le dossier est créé PENDING (« À valider ») sauf "status": "VALIDATED".
curl -X POST https://kaliio.fr/api/v1/sessions/SESSION_ID/enrollments \
-H "Authorization: Bearer $KALIIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client": { "name": "Atelier Martin", "type": "COMPANY", "email_contact": "[email protected]" },
"trainees": [
{ "first_name": "Camille", "last_name": "Martin", "email": "[email protected]" }
]
}'Créer une session
curl -X POST https://kaliio.fr/api/v1/sessions \
-H "Authorization: Bearer $KALIIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"formation_id": "FORMATION_ID",
"status": "DRAFT",
"location": "Vichy",
"max_participants": 8,
"session_dates": [
{ "date": "2026-12-02", "start_time": "09:00", "end_time": "12:30", "duration_hours": 3.5, "type": "MORNING" }
]
}'Pagination et erreurs
Les listes acceptent limit (1 à 100, 25 par défaut) et offset, et renvoient pagination.total et pagination.has_more. Toute erreur a la même forme :
HTTP/1.1 422
{
"error": {
"code": "validation_error",
"message": "Certains champs sont invalides.",
"details": [{ "field": "trainees.0.email", "message": "L'email n'est pas valide" }]
}
}| HTTP | code | Cause |
|---|---|---|
| 400 | bad_request | Corps non JSON, paramètre de requête invalide |
| 401 | unauthorized | Clé absente, inconnue ou révoquée |
| 403 | forbidden | Clé en lecture seule sur une écriture |
| 404 | not_found | Ressource absente de votre organisme, ou adresse inconnue |
| 422 | validation_error | Champs refusés : le détail est dans details[] |
| 429 | rate_limited | Limite de débit atteinte : attendre Retry-After secondes |
| 500 | internal_error | Erreur de notre côté : réessayer plus tard |
Limites
120 requêtes par minute et par clé. Chaque réponse porte X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset ; au-delà, l'API répond 429 avec Retry-After. 50 stagiaires au plus par inscription, 5 webhooks par organisme.
Webhooks
Ajoutez une adresse HTTPS publique dans Paramètres > Intégrations > Webhooks et choisissez les événements. Kaliio y envoie un POST JSON environ une minute après le fait. Répondez par un code 2xx en moins de 10 secondes ; sinon Kaliio réessaie après 1 min, 5 min, 30 min et 2 h (5 essais au plus, même corps, même id : dédoublonnez sur celui-ci). Après 3 événements de suite non livrés, le webhook est désactivé et l'organisme prévenu par email. Les redirections ne sont pas suivies ; les adresses privées ou locales sont refusées.
| Événement | Quand |
|---|---|
enrollment.created | Un dossier d'inscription (avec ses stagiaires) est créé sur une session. |
enrollment.validated | Un dossier d'inscription passe au statut « Validé ». |
session.scheduled | Une session passe au statut « Programmée ». |
session.completed | Une session passe au statut « Terminée ». |
document.signed | Un document envoyé à signer (devis, convention…) est signé en ligne. |
questionnaire.completed | Un quiz, un questionnaire de satisfaction (à chaud, à froid) ou une appréciation client ou financeur est rempli. |
attendance.signed | Un stagiaire ou un formateur signe sa présence sur un créneau. |
ping | Bouton « Envoyer un test » de Paramètres. |
data contient la ressource : dossier (comme la réponse d'inscription), session, document signé (id, title, signed_at…), questionnaire (kind, session_id, trainee_id, score…) ou émargement (session_id, slot, signer, signed_at).
POST https://votre-site.fr/webhooks/kaliio
Content-Type: application/json
User-Agent: Kaliio-Webhooks/1.0
Kaliio-Signature: t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Kaliio-Event-Id: 9d2c…
Kaliio-Event-Type: enrollment.created
Kaliio-Delivery-Attempt: 1
{
"id": "9d2c…",
"type": "enrollment.created",
"created_at": "2026-10-07T09:12:44.123Z",
"data": {
"id": "a41f…", "session_id": "6f1c…", "client_id": "c7d0…", "status": "PENDING",
"trainees": [{ "id": "…", "first_name": "Camille", "last_name": "Martin", "email": "…" }]
}
}Vérifier la signature
L'en-tête Kaliio-Signature vaut t=<horodatage>,v1=<signature>, où la signature est le HMAC-SHA256 hexadécimal de "<t>.<corps brut>" avec le secret du webhook (whsec_…, affiché à sa création). Calculez-la sur le corps exact reçu, comparez en temps constant, et refusez un horodatage de plus de 5 minutes (rejeu).
Node.js
import { createHmac, timingSafeEqual } from "node:crypto"
// rawBody : le corps EXACT reçu (chaîne), avant tout JSON.parse
export function verifyKaliioSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")))
const timestamp = Number(parts.t)
// Anti-rejeu : refuser un envoi de plus de 5 minutes
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex")
const signature = String(parts.v1 ?? "")
return (
signature.length === expected.length &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
)
}PHP
<?php
function verify_kaliio_signature(string $rawBody, string $header, string $secret): bool {
$parts = [];
foreach (explode(',', $header) as $part) {
[$key, $value] = array_pad(explode('=', $part, 2), 2, '');
$parts[$key] = $value;
}
$timestamp = (int) ($parts['t'] ?? 0);
// Anti-rejeu : refuser un envoi de plus de 5 minutes
if ($timestamp === 0 || abs(time() - $timestamp) > 300) {
return false;
}
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_KALIIO_SIGNATURE'] ?? '';
if (!verify_kaliio_signature($rawBody, $header, getenv('KALIIO_WEBHOOK_SECRET'))) {
http_response_code(400);
exit;
}
$event = json_decode($rawBody, true);
http_response_code(200);Formulaire d'inscription intégrable
Sans développement : l'organisme ouvre son formulaire dans Paramètres > Intégrations > Formulaire d'inscription en ligne, puis colle le code fourni. Le formulaire liste les sessions programmées non complètes ; ajoutez /<identifiant de session> à l'adresse pour une seule session. Chaque demande crée un dossier « À valider » et déclenche enrollment.created.
<iframe src="https://kaliio.fr/inscription/VOTRE-ORGANISME"
title="Inscription en ligne" width="100%" height="900"
style="border:0" loading="lazy"></iframe>