Passer au contenu

Guide du développeur

API REST de Quire, OAuth, webhooks, serveur MCP et extensions.

Afficher en Markdown

Utilisez l’adresse d’API de votre organisation et un justificatif dont la portée est limitée. Commencez par une requête de lecture, vérifiez la réponse et gardez les secrets à l’extérieur du contrôle de version et des exemples de documentation.

Quire offre une API publique : REST sur HTTPS, décrite par un document OpenAPI 3.1, avec des webhooks signés pour les événements et un serveur MCP pour les assistants d’IA. La référence de l’API répertorie tous les points de terminaison et événements.

Adresses

Chaque organisation a sa propre adresse, sous laquelle se trouve l’API :

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

Le justificatif détermine l’organisation. Une clé d’une organisation utilisée à l’adresse d’une autre est refusée.

Le document OpenAPI est accessible à /api/v1/openapi.json sur l’adresse de chaque organisation; les générateurs de clients voient donc toujours la version que vous appelez.

Authentification

Les clés d’API servent aux scripts et aux intégrations serveur à serveur. Un administrateur en crée une à /admin/integrations/api-keys, en choisit les portées et ne la voit qu’une seule 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 qui agissent au nom d’une personne connectée. Enregistrez un client à /admin/integrations/oauth-clients, puis utilisez le flux de code d’autorisation avec PKCE (/oauth/authorize, /oauth/token) ou les justificatifs client pour un client machine. La découverte se fait à /.well-known/oauth-authorization-server. Une portée limite ce qu’un jeton peut faire; 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 sont privilégiées et accompagnées d’un avertissement sur l’écran de consentement : audit:read, roles:write, tenants:write et users:delete.

The API keys page with one key, the person it acts as, its scopes and its status, and a form to create another.
API keys list who each key acts as and what it may reach.

Requêtes

  • Pagination : chaque liste est paginée par curseur. Transmettez limit, puis la valeur next_cursor de page comme 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 modifications postérieures à une heure donnée. Combinez-le avec include_deleted=true, ou lisez /<resource>/deletions, pour connaître les éléments supprimés.
  • Identifiants externes : la plupart des ressources acceptent votre propre external_id, et /<resource>/ext:{external_id} permet de les lire ou de les créer ou mettre à jour avec cet identifiant; 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 sans répéter l’opération. Les points de terminaison groupés l’exigent.
  • Versions : la version majeure figure dans le chemin (/v1). À l’intérieur de celle-ci, chaque changement incompatible est une révision datée, sélectionnée 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 à la création de votre justificatif.

Exemple de page d’une liste :

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

Erreurs

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 traitement sur code, qui est stable; detail est destiné aux personnes, peut leur être affiché et peut changer. Si vous ne reconnaissez pas un code, classez l’erreur selon category :

Catégorie État Nouvelle tentative
validation 422, avec les détails des champs dans errors No
authentication 401 No
authorization 403 No
not_found 404 No
conflict 409 Sometimes
precondition 412 No
quota 402 pour le forfait, 413 pour la taille No
rate_limit 429, avec Retry-After Yes
upstream 502 ou 504 Yes
internal 500 Yes

Indiquez request_id lorsque vous communiquez avec le soutien.

Webhooks

Abonnez-vous à /admin/webhooks ou au moyen de l’API, à /webhook_subscriptions. Choisissez les événements par nom (enrolment.created), par groupe (enrolment.*) ou tous (*). Quire envoie d’abord un webhook.ping; l’abonnement commence lorsque 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. Construisez la chaîne {webhook-id}.{webhook-timestamp}.{raw body} à partir des octets exacts reçus, avant toute analyse JSON.
  2. Calculez HMAC-SHA256 sur cette chaîne avec le secret de votre abonnement, puis encodez le résultat en base64.
  3. Comparez en temps constant chaque valeur v1, de webhook-signature. Il peut y en avoir deux pendant la rotation d’un secret; une seule correspondance suffit.
  4. Rejetez un horodatage qui s’écarte de plus de cinq minutes de votre horloge.
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 sont réessayées avec un délai progressif jusqu’à 72 heures et peuvent être rejouées à partir du journal de livraison.

MCP

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

The AI assistants page with the server address to give an assistant and a table of the tools it can use.
AI assistants (MCP): the server address, and the tools an assistant may call.

Forfaits et API

Les clés d’API, clients OAuth, webhooks et serveur MCP relèvent du droit d’accès à l’API du forfait, qui est compris dans tous les forfaits standards. Sans ce droit, 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 continuent afin que les données restent exportables. Le refus est un document de problème dont le code est commerce.plan_entitlement, dans la catégorie precondition.

Extensions

Les types d’activité, blocs, méthodes d’inscription, méthodes de connexion, types de questions, rapports, thèmes et intégrations propres à Quire sont déclarés par le même registre d’extensions auquel une installation autohébergée peut ajouter des entrées. Les extensions sont intégrées à la compilation : il n’y a pas de chargeur de greffons à l’exécution, et une organisation hébergée ne peut pas en ajouter. Les administrateurs activent ou désactivent chaque extension pour leur organisation à /admin/extensions (consultez le guide de l’administrateur).

Pour en créer 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, les éléments qu’elle fournit et exige, et l’autorisation ou non pour une organisation de la désactiver. Inscrivez-la à l’endroit où l’application Web et le processus de travail sont assemblés, afin qu’ils soient tous deux d’accord. Le registre vérifie les règles propres à chaque point lors de sa création et à chaque appel de register, refuse tout ensemble invalide en nommant chaque problème et reste inchangé en cas de refus. Les tests de l’extension doivent vérifier que extensionContractProblems est vide pour celle-ci et que sa désactivation modifie les éléments concernés.

Navigation

Saisir pour rechercher…

↑↓ naviguer↵ sélectionnerEsc fermer