---
title: "Guide du développeur"
description: "L’API REST de Quire, OAuth, les webhooks, le serveur MCP et les extensions."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Guide du développeur

<span id="developer-guide"></span>

Utilisez l’adresse API de votre organisation et des identifiants aux portées limitées. Commencez par une requête de lecture, vérifiez la réponse et gardez les secrets hors du contrôle de version et des exemples de documentation.

Quire propose une API publique : REST sur HTTPS, décrite dans un document OpenAPI 3.1, des webhooks signés pour les événements et un serveur MCP pour les assistants d’IA. La [référence de l’API](https://docs.quirelms.com/api/) répertorie tous les points de terminaison et événements.

## Adresses <!--quire:addresses-->

Chaque organisation dispose de sa propre adresse, qui héberge l’API :

```
https://acme.quirelms.com/api/v1/courses
```

Les identifiants déterminent l’organisation. Une clé utilisée à l’adresse d’une autre organisation est refusée.

Le document OpenAPI est publié à `/api/v1/openapi.json` sur l’adresse de chaque organisation ; les générateurs de clients consultent donc toujours la version appelée.

## Authentification <!--quire:authentication-->

Les **clés API** servent aux scripts et aux intégrations de serveur à serveur. Un administrateur en crée une dans `/admin/integrations/api-keys`, en choisit les portées et ne peut la voir qu’une fois. Envoyez-la comme jeton porteur :

```
curl -H "Authorization: Bearer qk_live_..." https://acme.quirelms.com/api/v1/users?limit=50
```

Les clés commencent par `qk_live_` ou `qk_test_`. Attribuez une clé distincte à chaque intégration.

**OAuth 2.1** sert aux applications agissant au nom d’une personne connectée. Enregistrez un client dans `/admin/integrations/oauth-clients`, puis utilisez le flux de code d’autorisation avec PKCE (`/oauth/authorize`, `/oauth/token`), ou les identifiants du client pour un client machine. Le document de découverte est publié dans `/.well-known/oauth-authorization-server`. Une portée restreint les opérations du jeton ; elle ne lui permet jamais d’en faire plus que la personne.

Les portées sont `resource:read`, `resource:write` et `resource:delete`, par exemple `courses:read` ou `enrolments:write`. Quatre portées privilégiées sont accompagnées d’un avertissement sur l’écran de consentement : `audit:read`, `roles:write`, `tenants:write` et `users:delete`.

<figure class="quire-shot" lang="en" dir="ltr"><img src="/screenshots/admin-api-keys.webp" alt="The API keys page with one key, the person it acts as, its scopes and its status, and a form to create another." width="944" height="700" loading="lazy" decoding="async"><figcaption>API keys list who each key acts as and what it may reach.</figcaption></figure>

## Requêtes <!--quire:requests-->

- **Pagination** : chaque liste utilise une pagination par curseur. Indiquez `limit`, puis transmettez `next_cursor` de `page` en tant que `cursor` tant que `has_more` vaut true (voir l’exemple ci-dessous). Il n’y a pas de décalage.
- **Modifications depuis une date** : `updated_since` renvoie les changements postérieurs à un instant. Associez-le à `include_deleted=true`, ou consultez `/<resource>/deletions`, pour connaître les suppressions.
- **Identifiants externes** : la plupart des ressources acceptent votre propre `external_id` et `/<resource>/ext:{external_id}` permet de lire ou de créer/mettre à jour une ressource à partir de celui-ci ; une synchronisation n’a donc pas besoin de conserver les identifiants Quire.
- **Idempotence** : envoyez un en-tête `Idempotency-Key` avec `POST`, `PATCH` et `DELETE`. Une nouvelle tentative avec la même clé renvoie la première réponse au lieu de répéter l’opération. Les points de terminaison groupés l’exigent.
- **Versions** : la version majeure figure dans le chemin (`/v1`). Elle contient des révisions datées pour chaque modification incompatible, sélectionnées avec l’en-tête `Quire-Version`, par exemple `Quire-Version: 2026-09-20`. Sans cet en-tête, vous obtenez la révision en vigueur lors de l’émission de vos identifiants.

Exemple de page d’une liste :

```
{"data": [...], "page": {"next_cursor": "eyJ2Ijox...", "has_more": true, "limit": 100}}
```

## Erreurs <!--quire:errors-->

Chaque erreur est un document de problème RFC 9457 :

```
{"type": "https://quire.com/errors/enrolment.seat_limit_reached",
 "title": "Seat limit reached", "status": 409,
 "code": "enrolment.seat_limit_reached", "category": "conflict",
 "detail": "The course has no seats left, so this enrolment was not created. ...",
 "request_id": "01JB7XQK4Z..."}
```

Fondez votre logique sur `code`, qui est stable ; `detail` est rédigé pour les personnes, peut leur être affiché et peut changer. Si vous ne connaissez pas un code, utilisez sa `category` :

| Catégorie | Statut | Nouvelle tentative |
| --- | --- | --- |
| `validation` | 422, avec le détail des champs dans `errors` | Non |
| `authentication` | 401 | Non |
| `authorization` | 403 | Non |
| `not_found` | 404 | Non |
| `conflict` | 409 | Parfois |
| `precondition` | 412 | Non |
| `quota` | 402 pour le forfait, 413 pour la taille | Non |
| `rate_limit` | 429, avec `Retry-After` | Oui |
| `upstream` | 502 ou 504 | Oui |
| `internal` | 500 | Oui |

Indiquez `request_id` lorsque vous contactez l’assistance.

## Webhooks <!--quire:webhooks-->

Créez un abonnement dans `/admin/webhooks` ou via l’API à `/webhook_subscriptions`. Choisissez les événements par nom (`enrolment.created`), par domaine (`enrolment.*`) ou tous (`*`). Quire envoie d’abord un événement `webhook.ping` ; l’abonnement démarre quand votre point de terminaison y répond.

Les livraisons suivent la spécification Standard Webhooks :

```
POST /hooks/quire
webhook-id: 01JB7XQK4Z8FQ2M3N4P5R6S7T8
webhook-timestamp: 1790000000
webhook-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=
```

Pour vérifier une livraison :

1. À partir des octets exacts reçus, avant tout décodage JSON, construisez la chaîne `{webhook-id}.{webhook-timestamp}.{raw body}`.
2. Calculez son HMAC-SHA256 avec le secret de votre abonnement, puis encodez-le en base64.
3. Comparez en temps constant le résultat à chaque valeur `v1,` de `webhook-signature`. Il peut y en avoir deux pendant la rotation d’un secret ; une seule correspondance suffit.
4. Rejetez tout horodatage qui diffère de votre horloge de plus de cinq minutes.

```
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(secret, id, timestamp, rawBody, header) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = createHmac('sha256', Buffer.from(secret.replace(/^whsec_/, ''), 'base64'))
    .update(`${id}.${timestamp}.${rawBody}`).digest();
  return header.split(' ').some((part) => {
    const [version, value] = part.split(',');
    const given = Buffer.from(value ?? '', 'base64');
    return version === 'v1' && given.length === expected.length && timingSafeEqual(given, expected);
  });
}
```

Éliminez les doublons à l’aide de `webhook-id` : une livraison peut arriver plusieurs fois. Le corps contient des identifiants et un bref résumé ; récupérez la ressource pour connaître son état actuel. Les livraisons échouées font l’objet de nouvelles tentatives avec temporisation pendant un maximum de 72 heures ; vous pouvez également les rejouer depuis le journal de distribution.

## MCP <!--quire:mcp-->

Le serveur MCP de Quire se trouve à `/mcp` sur l’adresse de l’organisation, et utilise HTTP en flux. Un client MCP découvre le serveur OAuth dans `/.well-known/oauth-protected-resource` ; la personne se connecte et donne son consentement comme avec tout client OAuth. Les outils agissent selon les autorisations de cette personne, et les outils destructifs demandent confirmation. Les administrateurs choisissent les outils disponibles dans `/admin/integrations/mcp`.

<figure class="quire-shot" lang="en" dir="ltr"><img src="/screenshots/admin-mcp.webp" alt="The AI assistants page with the server address to give an assistant and a table of the tools it can use." width="944" height="700" loading="lazy" decoding="async"><figcaption>AI assistants (MCP): the server address, and the tools an assistant may call.</figcaption></figure>

## Forfaits et API <!--quire:plans-and-the-api-->

Les clés API, clients OAuth, webhooks et le serveur MCP relèvent de l’option API du forfait, incluse dans chaque forfait standard. Dans un forfait qui ne l’inclut pas, la création d’une clé, d’un client ou d’un abonnement est refusée, les écritures REST et les connexions MCP sont refusées, mais les lectures REST restent possibles afin que les données puissent être exportées. Le refus est un document de problème avec le code `commerce.plan_entitlement`, dans la catégorie `precondition`.

## Extensions <!--quire:extensions-->

Les types d’activité, blocs, méthodes d’inscription, modes de connexion, types de question, rapports, thèmes et intégrations de Quire sont déclarés dans le même registre d’extensions que peut enrichir une installation auto-hébergée. Les extensions sont intégrées à la compilation : aucun chargeur de modules n’est disponible à l’exécution, et une organisation utilisant Quire hébergé ne peut pas en ajouter. Les administrateurs activent ou désactivent chaque extension pour leur organisation dans `/admin/extensions` (consultez le [guide de l’administrateur](/fr/admin/extensions/)).

Pour en écrire une, partez du bloc et du thème d’exemple dans `packages/integration/extensions/src/sample.ts`. Choisissez le point d’extension et lisez son contrat dans `points.ts`, puis déclarez l’extension avec un identifiant, une version, une licence, ses éléments fournis et requis, et l’autorisation ou non pour une organisation de la désactiver. Enregistrez-la à l’endroit où l’application Web et le worker sont assemblés afin qu’ils partagent la même configuration. À la construction et à chaque appel à `register`, le registre vérifie les règles propres à chaque point d’extension, refuse en nommant tous les problèmes un ensemble invalide et conserve le registre inchangé. Les tests de l’extension doivent vérifier que `extensionContractProblems` ne renvoie aucun problème et que la désactivation modifie les éléments concernés.

Source: https://docs.quirelms.com/fr/developer/index.mdx
