Aller au contenu principal

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

Points d'accès de l'API REST
MéthodeCheminPortéeRôle
GET/formationslectureCatalogue (filtres search, published)
GET/formations/{id}lectureUne formation
GET/sessionslectureSessions à venir, places restantes (filtres from, to, status, formation_id)
GET/sessions/{id}lectureUne session et ses créneaux
GET/sessions/{id}/traineeslectureStagiaires inscrits et leur dossier
POST/sessionsécritureCréer une session d'une formation existante
POST/sessions/{id}/enrollmentsécritureInscrire des stagiaires (dossier « À valider » par défaut)
GET/clientslectureClients (filtres search, email)
GET/clients/{id}lectureUn client
POST/clientsécritureCréer un client
PATCH/clients/{id}écritureModifier 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" }]
  }
}
Codes d'erreur
HTTPcodeCause
400bad_requestCorps non JSON, paramètre de requête invalide
401unauthorizedClé absente, inconnue ou révoquée
403forbiddenClé en lecture seule sur une écriture
404not_foundRessource absente de votre organisme, ou adresse inconnue
422validation_errorChamps refusés : le détail est dans details[]
429rate_limitedLimite de débit atteinte : attendre Retry-After secondes
500internal_errorErreur 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énements des webhooks
ÉvénementQuand
enrollment.createdUn dossier d'inscription (avec ses stagiaires) est créé sur une session.
enrollment.validatedUn dossier d'inscription passe au statut « Validé ».
session.scheduledUne session passe au statut « Programmée ».
session.completedUne session passe au statut « Terminée ».
document.signedUn document envoyé à signer (devis, convention…) est signé en ligne.
questionnaire.completedUn quiz, un questionnaire de satisfaction (à chaud, à froid) ou une appréciation client ou financeur est rempli.
attendance.signedUn stagiaire ou un formateur signe sa présence sur un créneau.
pingBouton « 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>