Aller au contenu principal
QualiForma
CatalogueDevenir formateurQualiopiIADéveloppeurs
Connexion
  • Catalogue
  • Devenir formateur
  • Qualiopi
  • IA
  • Développeurs
  • Connexion
  • Vue d'ensemble

Démarrage

  • Quickstart
  • Authentification

Référence

  • API Reference (Scalar)

Endpoints

  • Tous les endpoints

Cœur LMS

  • Tenants
  • Utilisateurs
  • Formations
  • Inscriptions
  • Sessions live

Conformité Qualiopi

  • Dashboard Qualiopi
  • Conformité
  • Émargement
  • Questionnaires
  • Réclamations
  • Plans d'amélioration
  • Parcours adaptatifs
  • Compétences formateurs
  • BPF
  • Médiateurs

Paiements & Facturation

  • Paiements
  • Facturation Factur-X
  • Webhooks

Design System

  • Vue d'ensemble
  • Couleurs
  • Typographie
  • Espacement
  • Elevation
  • Motion
  • Radius
  • Composants
  • · Formulaires
  • · Feedback
  • · Navigation
  • · Progression
  • · Données
Swagger UI (s'ouvre dans un nouvel onglet)
  1. Développeurs
  2. Quickstart

Quickstart

De zéro à votre premier appel API en moins de 5 minutes.

Prérequis

  • Un compte QualiForma actif avec votre slug tenant
  • Python 3.9+ avec pip install requests, ou Node.js 18+, ou curl
1

Étape 1 : Obtenir un access token

Authentifiez-vous avec vos identifiants pour recevoir un JWT.

POST /auth/login · Python
import requests

# 1. S'authentifier
response = requests.post(
    'https://api.qualiforma.site/api/v1/auth/login',
    headers={'X-Tenant-ID': 'votre-tenant'},
    json={
        'email': 'admin@example.com',
        'password': 'votre-mot-de-passe'
    }
)
data = response.json()['data']
access_token = data['accessToken']
refresh_token = data['refreshToken']

print(f"Access token: {access_token[:40]}...")
# Access token valide 1h — le rafraîchir avec refreshToken
POST /auth/login · cURL / Shell
curl -X POST https://api.qualiforma.site/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: votre-tenant' \
  -d '{
    "email": "admin@example.com",
    "password": "votre-mot-de-passe"
  }'

# Réponse attendue :
# {
#   "data": {
#     "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
#     "refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
#     "expiresIn": 3600
#   },
#   "meta": {
#     "timestamp": "2026-01-01T12:00:00.000Z",
#     "requestId": "req_abc123"
#   }
# }
POST /auth/login · TypeScript
// Typages
interface AuthResponse {
  data: {
    accessToken: string;
    refreshToken: string;
    expiresIn: number;
  };
  meta: { timestamp: string; requestId: string };
}

async function login(email: string, password: string, tenantId: string) {
  const response = await fetch('https://api.qualiforma.site/api/v1/auth/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Tenant-ID': tenantId,
    },
    body: JSON.stringify({ email, password }),
  });

  if (!response.ok) {
    throw new Error(`Login échoué : ${response.status}`);
  }

  const json: AuthResponse = await response.json();
  return json.data;
}

const tokens = await login('admin@example.com', 'mot-de-passe', 'votre-tenant');
console.log('Access token:', tokens.accessToken.slice(0, 40) + '...');
POST /auth/login · PHP
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

$client = new Client(['base_uri' => 'https://api.qualiforma.site/api/v1/']);

try {
    $response = $client->post('auth/login', [
        'headers' => [
            'X-Tenant-ID' => 'votre-tenant',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'email' => 'admin@example.com',
            'password' => 'votre-mot-de-passe',
        ],
    ]);
    $body = json_decode($response->getBody()->getContents(), true);
    $accessToken = $body['data']['accessToken'];
    $refreshToken = $body['data']['refreshToken'];

    echo "Access token: " . substr($accessToken, 0, 40) . "...\n";
    // Access token valide 1h — le rafraichir avec refreshToken
} catch (GuzzleException $e) {
    fwrite(STDERR, "Login failed: " . $e->getMessage() . PHP_EOL);
}

Note : Le token expire après 1h. Utilisez refreshToken pour en obtenir un nouveau sans re-login.

2

Étape 2 : Récupérer votre profil

Le login ne retourne que les tokens — appelez /auth/me pour le profil utilisateur.

GET /auth/me · Python
# 2. Récupérer le profil utilisateur
me_response = requests.get(
    'https://api.qualiforma.site/api/v1/auth/me',
    headers={
        'Authorization': f'Bearer {access_token}',
        'X-Tenant-ID': 'votre-tenant'
    }
)
me = me_response.json()['data']

print(f"Connecté en tant que : {me['firstName']} {me['lastName']}")
print(f"Email : {me['email']}")
print(f"Rôle  : {me['role']}")
GET /auth/me · cURL / Shell
# Remplacer YOUR_TOKEN par l'accessToken obtenu à l'étape 1
TOKEN="eyJhbGciOiJIUzI1NiJ9..."

curl https://api.qualiforma.site/api/v1/auth/me \
  -H "Authorization: Bearer $TOKEN" \
  -H 'X-Tenant-ID: votre-tenant'

# Réponse :
# {
#   "data": {
#     "id": "usr_...",
#     "email": "admin@example.com",
#     "firstName": "Marie",
#     "lastName": "Martin",
#     "role": "ADMIN"
#   }
# }
GET /auth/me · TypeScript
async function getMe(accessToken: string, tenantId: string) {
  const response = await fetch('https://api.qualiforma.site/api/v1/auth/me', {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'X-Tenant-ID': tenantId,
    },
  });

  if (!response.ok) throw new Error(`Erreur ${response.status}`);
  const { data } = await response.json();
  return data;
}

const profile = await getMe(tokens.accessToken, 'votre-tenant');
console.log(`Bonjour ${profile.firstName} (${profile.role})`);
GET /auth/me · PHP
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

$client = new Client(['base_uri' => 'https://api.qualiforma.site/api/v1/']);

try {
    $response = $client->get('auth/me', [
        'headers' => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-Tenant-ID' => 'votre-tenant',
        ],
    ]);
    $body = json_decode($response->getBody()->getContents(), true);
    $me = $body['data'];

    echo "Connecte en tant que : {$me['firstName']} {$me['lastName']}\n";
    echo "Email : {$me['email']}\n";
    echo "Role  : {$me['role']}\n";
} catch (GuzzleException $e) {
    fwrite(STDERR, "Erreur: " . $e->getMessage() . PHP_EOL);
}
3

Étape 3 : Lister les formations

Récupérez le catalogue avec pagination, filtres statut, recherche texte.

GET /courses · Python
# 3. Lister les formations
courses_response = requests.get(
    'https://api.qualiforma.site/api/v1/courses',
    headers={
        'Authorization': f'Bearer {access_token}',
        'X-Tenant-ID': 'votre-tenant'
    },
    params={
        'page': 1,
        'perPage': 10,
        'status': 'PUBLISHED'  # optionnel
    }
)
result = courses_response.json()
courses = result['data']

for course in courses:
    print(f"- {course['title']} (id: {course['id']})")
GET /courses · cURL / Shell
TOKEN="eyJhbGci..."

curl "https://api.qualiforma.site/api/v1/courses?page=1&perPage=10" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'X-Tenant-ID: votre-tenant'

# Réponse :
# {
#   "data": [
#     {
#       "id": "crs_abc123",
#       "title": "Excel Avancé pour RH",
#       "status": "PUBLISHED",
#       "price": 49900,
#       "currency": "EUR"
#     }
#   ],
#   "meta": {
#     "total": 24,
#     "page": 1,
#     "perPage": 10
#   }
# }
GET /courses · TypeScript
interface Course {
  id: string;
  title: string;
  status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
  price: number;
  currency: string;
}

async function listCourses(
  accessToken: string,
  tenantId: string,
  params?: { page?: number; perPage?: number; status?: string }
): Promise<{ data: Course[]; meta: { total: number } }> {
  const searchParams = new URLSearchParams({
    page: String(params?.page ?? 1),
    perPage: String(params?.perPage ?? 10),
    ...(params?.status ? { status: params.status } : {}),
  });

  const response = await fetch(
    `https://api.qualiforma.site/api/v1/courses?${searchParams}`,
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'X-Tenant-ID': tenantId,
      },
    }
  );

  if (!response.ok) throw new Error(`Erreur ${response.status}`);
  return response.json();
}

const { data: courses, meta } = await listCourses(tokens.accessToken, 'votre-tenant');
console.log(`${meta.total} formations trouvées :`);
courses.forEach((c) => console.log(`  - ${c.title} (${c.id})`));
GET /courses · PHP
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

$client = new Client(['base_uri' => 'https://api.qualiforma.site/api/v1/']);

try {
    $response = $client->get('courses', [
        'headers' => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-Tenant-ID' => 'votre-tenant',
        ],
        'query' => [
            'page' => 1,
            'perPage' => 10,
            'status' => 'PUBLISHED', // optionnel
        ],
    ]);
    $result = json_decode($response->getBody()->getContents(), true);
    $courses = $result['data'];

    foreach ($courses as $course) {
        echo "- {$course['title']} (id: {$course['id']})\n";
    }
} catch (GuzzleException $e) {
    fwrite(STDERR, "Erreur: " . $e->getMessage() . PHP_EOL);
}

Note : Le champ price est en centimes (49900 = 499,00 €).

4

Étape 4 : Inscrire un apprenant

Créez une inscription directement (FREE) ou via un flux de paiement.

POST /enrollments · Python
# 4. Inscrire un apprenant à une formation
enroll_response = requests.post(
    'https://api.qualiforma.site/api/v1/enrollments',
    headers={
        'Authorization': f'Bearer {access_token}',
        'X-Tenant-ID': 'votre-tenant',
        'Content-Type': 'application/json'
    },
    json={
        'courseId': 'crs_abc123',       # ID de la formation
        'userId': 'usr_xyz789',         # ID de l'apprenant
        'paymentMethod': 'FREE'         # FREE | VIVA_ISV | STRIPE
    }
)
enrollment = enroll_response.json()['data']
print(f"Inscription créée : {enrollment['id']}")
print(f"Statut : {enrollment['status']}")  # ACTIVE
POST /enrollments · cURL / Shell
TOKEN="eyJhbGci..."

curl -X POST https://api.qualiforma.site/api/v1/enrollments \
  -H "Authorization: Bearer $TOKEN" \
  -H 'X-Tenant-ID: votre-tenant' \
  -H 'Content-Type: application/json' \
  -d '{
    "courseId": "crs_abc123",
    "userId": "usr_xyz789",
    "paymentMethod": "FREE"
  }'

# Réponse 201 :
# {
#   "data": {
#     "id": "enr_def456",
#     "status": "ACTIVE",
#     "courseId": "crs_abc123",
#     "userId": "usr_xyz789",
#     "enrolledAt": "2026-01-01T12:30:00Z"
#   }
# }
POST /enrollments · TypeScript
interface EnrollmentInput {
  courseId: string;
  userId: string;
  paymentMethod: 'FREE' | 'VIVA_ISV' | 'STRIPE';
}

async function enrollUser(
  input: EnrollmentInput,
  accessToken: string,
  tenantId: string
) {
  const response = await fetch('https://api.qualiforma.site/api/v1/enrollments', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${accessToken}`,
      'X-Tenant-ID': tenantId,
    },
    body: JSON.stringify(input),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message ?? `Erreur ${response.status}`);
  }

  const { data } = await response.json();
  return data;
}

const enrollment = await enrollUser(
  { courseId: 'crs_abc123', userId: 'usr_xyz789', paymentMethod: 'FREE' },
  tokens.accessToken,
  'votre-tenant'
);

console.log(`Inscription ${enrollment.id} créée — statut : ${enrollment.status}`);
POST /enrollments · PHP
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

$client = new Client(['base_uri' => 'https://api.qualiforma.site/api/v1/']);

try {
    $response = $client->post('enrollments', [
        'headers' => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-Tenant-ID' => 'votre-tenant',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'courseId' => 'crs_abc123',      // ID de la formation
            'userId' => 'usr_xyz789',        // ID de l'apprenant
            'paymentMethod' => 'FREE',       // FREE | VIVA_ISV | STRIPE
        ],
    ]);
    $enrollment = json_decode($response->getBody()->getContents(), true)['data'];
    echo "Inscription creee : {$enrollment['id']}\n";
    echo "Statut : {$enrollment['status']}\n";
} catch (GuzzleException $e) {
    fwrite(STDERR, "Erreur: " . $e->getMessage() . PHP_EOL);
}

Note : Requiert le rôle ADMIN ou CREATOR. LEARNER ne peut s'inscrire que via le formulaire web.

Prochaines étapes

  • Authentification avancée

    Refresh tokens, OIDC headless

  • Endpoints formations

    CRUD complet + schémas

  • Webhooks

    Événements temps-réel + HMAC

  • API Reference

    Explorer tous les endpoints

QualiForma

La plateforme de formation professionnelle certifiee Qualiopi.

Plateforme

  • Catalogue
  • Espace formateur
  • Étude de besoin

Societe

  • A propos
  • Contact

Ressources

  • Développeurs
  • Référence API
  • Webhooks

Legal

  • CGV
  • Mentions legales
  • Confidentialite

Catalogue par catégorie

  • Management
  • Digital
  • Communication
  • Langues
  • Sécurité
  • Gestion

Comparatifs

  • QualiForma vs Didask
  • QualiForma vs Edusign
  • QualiForma vs Klaxoon
  • QualiForma vs 360Learning

Glossaire Qualiopi

  • I01 — Conditions d'information
  • I05 — Adaptation des prestations
  • I11 — Évaluations en cours
  • I22 — Compétences des intervenants
  • I30 — Recueil des appréciations
  • Voir les 32 indicateurs →

© 2026 QualiForma. Tous droits reserves.

Certifie Qualiopi