---
title: "Entwéckler-Guide"
description: "Déi Quire REST API, OAuth, Webhooks, de MCP-Server an Erweiderungen."
image: "https://docs.quirelms.com/og.png"
---

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

# Entwéckler-Guide

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

Benotzt d'API-Adress vun Ärer Organisatioun an eng Identifikatioun mat bestëmmte Scopes. Fänkt mat enger Liesdemande un, kontrolléiert d'Äntwert, a bleift mat Geheimnisser ausserhalb vum Source Control an den Exempeler vun der Dokumentatioun.

Quire huet eng eenzeg ëffentlech API: REST iwwer HTTPS, beschriwwen an engem OpenAPI 3.1-Dokument, mat signéierte Webhooks fir Evenementer an engem MCP-Server fir KI-Assistenten. D'[API-Referenz](https://docs.quirelms.com/api/) list all Endpunkt an all Evenement.

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

All Organisatioun huet hir eegen Adress, an d'API läift do ënner:

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

D'Identifikatioun entscheet d'Organisatioun. En Schlëssel fir eng Organisatioun, deen op der Adress vun enger anerer benotzt gëtt, gëtt refuséiert.

D'OpenAPI-Dokument gëtt ëm `/api/v1/openapi.json` op der Adress vun all Organisatioun servéiert, sou datt Client-Generatoren ëmmer d'Versioun gesinn, déi Dir urufft.

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

**API-Schlësselen** sinn fir Skripten an Integratiounen tëscht Serveren. En Administrator erstellt en ëm `/admin/integrations/api-keys`, wielt seng Scopes a gesäit en eemol. Schéckt en als Bearer-Token:

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

Schlësselen ufänken mat `qk_live_` oder `qk_test_`. Gitt all Integratioun hiren eegene Schlëssel.

**OAuth 2.1** ass fir Applikatiounen, déi handelen wéi eng Persoun, déi sech aloggt. Registréiert en Client ëm `/admin/integrations/oauth-clients`, benutzt dunn de Autoriséierungscode-Flow mat PKCE (`/oauth/authorize`, `/oauth/token`), oder client credentials fir en Maschinn-Client. D'Discovery ass ëm `/.well-known/oauth-authorization-server`. E Scope limitéiert, wat e Token därf maachen; en léisst en ni méi maachen, wéi d'Persoun selwer därf.

Scopes sinn `resource:read`, `resource:write` an `resource:delete`, zum Beispill `courses:read` oder `enrolments:write`. Véier sinn privilegéiert an ginn mat enger Warnung op der Zoustëmmungssäit geweist: `audit:read`, `roles:write`, `tenants:write` an `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>

## Demanden <!--quire:requests-->

- **Paginéierung**: all Lëschten hunn Cursor-Paginéierung. Gitt `limit`,
  dann de `next_cursor` vun `page` als `cursor`, sou laang `has_more` wäert
  ass (Beispill hei drënner). Et gëtt keng Offset.
- **Ännerungen zënter**: `updated_since` gëtt zréck, wat no enger Zäit
  geännert huet. Koppelt en mat `include_deleted=true`, oder liest
  `/<resource>/deletions`, fir erauszefannen, wat ewechgeholl gouf.
- **Extern Identifikatiounen**: déi meescht Ressourcen akzeptéieren Ären
  eegenen `external_id`, an `/<resource>/ext:{external_id}` liest oder
  schreibt no ihm, sou datt e Sync ni d'Identifikatiounen vun Quire muss
  späicheren.
- **Idempotenz**: schéckt en `Idempotency-Key`-Header op `POST`, `PATCH` an
  `DELETE`. En neien Attempt mam selwechte Schlëssel gëtt déi éischt Äntwert
  zréck, amplaz d'Aarbecht zweemol ze maachen. Bulk-Endpunkter erfuerderen en.
- **Versiouen**: d'Haaptversioun ass am Wee (`/v1`). Bannendrem ass all
  Ännerung, déi d'Kompatibilitéit briicht, eng datéiert Revisioun, gewielt
  mam `Quire-Version`-Header, zum Beispill `Quire-Version: 2026-09-20`. Ouni
  de Header kritt Dir déi Revisioun, déi aktuell war, wou Är Identifikatioun
  erausginn gouf.

Eng Säit vun enger Lëschten:

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

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

All Feeler ass e Problem-Dokument no 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..."}
```

Zweigt no `code` of, deen stabil ass; `detail` ass fir Mënsche geschriwwen, sécher fir hinnen ze weisen, a kann se veränneren. Wann Dir e Code net kennt, gruppéiert no `category`:

| Kategorie | Status | Erëmprobéieren |
| --- | --- | --- |
| `validation` | 422, mat Felddetails an `errors` | Nei |
| `authentication` | 401 | Nei |
| `authorization` | 403 | Nei |
| `not_found` | 404 | Nei |
| `conflict` | 409 | Manchmal |
| `precondition` | 412 | Nei |
| `quota` | 402 fir de Plang, 413 fir d'Gréisst | Nei |
| `rate_limit` | 429, mat `Retry-After` | Jo |
| `upstream` | 502 oder 504 | Jo |
| `internal` | 500 | Jo |

Zitéiert `request_id`, wann Dir Iech bei Support mellt.

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

Abonniéiert ëm `/admin/webhooks`, oder iwwer d'API ëm `/webhook_subscriptions`. Wielt d'Evenementer no Numm (`enrolment.created`), no Beräich (`enrolment.*`) oder all (`*`). Quire schéckt éischte e `webhook.ping`; d'Abonnement start, sou wéi Äre Endpoint drop äntwert.

D'Liwwerunge folgen der Standard-Webhooks-Spezifikatioun:

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

Fir eng Liwwerung ze iwwerpréiwen:

1. Baut de String `{webhook-id}.{webhook-timestamp}.{raw body}` aus den
   exakten Bytes, déi kritt gi sinn, ier iergendeen JSON parsed gëtt.
2. Rechent HMAC-SHA256 driwwer mat Ärem Abonnement-Geheimnis a base64 et.
3. Vergläicht mat all `v1,`-Wäert an `webhook-signature` an konstanter Zäit.
   Do kënne zwee stoen wärend enger Rotatioun vum Geheimnis; wat och ëmmer
   passt, ass valabel.
4. Refuséiert en Zäitstempel, deen vun Ärer Auer méi wéi fënnef Minutten
   ewech läit.

```
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);
  });
}
```

Deduplizéiert no `webhook-id`: eng Liwwerung kann méi wéi eng Kéier ukommen. De Body dréit Identifikatiounen an eng kuerz Zesummefassung; holt d'Ressource fir hiren aktuellen Zoustand. Feelgeschloen Liwwerungen ginn mat Backoff bis zu 72 Stonnen erëmprobéiert, a kënne vum Liwwerungslog erëmgespillt ginn.

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

De MCP-Server vun Quire ass ëm `/mcp` op der Adress vun der Organisatioun, iwwer streamable HTTP. E MCP-Client fënnt de OAuth-Server ëm `/.well-known/oauth-protected-resource` eraus, an d'Persoun mellt sech un a gëtt hir Zoustëmmung, wéi bei all OAuth-Client. Tools handelen wéi dës Persoun, mat hiren Permissionen, a destruktiv Tools froen no enger Bestätegung. Administrateuren entscheeden, wéieng Tools ëm `/admin/integrations/mcp` disponibel sinn.

<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>

## Plang an d'API <!--quire:plans-and-the-api-->

API-Schlësselen, OAuth-Clienten, Webhooks an de MCP-Server gehéieren zum API-Recht vum Plang, an all Standardplang ëmfasst en. Op engem Plang ouni en gëtt d'Maache vun engem Schlëssel, Client oder Abonnement refuséiert, Schreiwen iwwer REST an MCP-Verbindungen ginn refuséiert, an Liesen iwwer REST funktionéieren weider, sou datt d'Donnéeën exportéierbar bleiwen. De Refus ass e Problem-Dokument mat dem Code `commerce.plan_entitlement`, an der Kategorie `precondition`.

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

D'eegenen Aktivitéitsaarten, Blocken, Aschreiwungsmethoden, Umeldungsmethoden, Froenaaarten, Rapporten, Themen an Integratiounen vun Quire ginn duerch dat selwecht Erweiderungsregister declaréiert, zu dem eng selwer gehostet Installatioun och derbäisetze kann. Erweiderungen ginn mat kompiléiert: et gëtt keng Laufzäit-Plugin-Lader, an eng gehostet Organisatioun kann en net derbäisetzen. Administrateuren schalten all Erweiderung fir hir Organisatioun un oder aus ëm `/admin/extensions` (kuckt den [Guide fir Administrateuren](/lb/admin/extensions/)).

Fir eng ze schreiwen, fänkt mat dem Beispill-Block an dem Beispill-Thema an `packages/integration/extensions/src/sample.ts`. Wielt d'Erweiderungs-Plaz an liest säin Vertrag an `points.ts`, a declaréiert dunn d'Erweiderung mat enger ID, enger Versioun, enger Lizenz, wat se bitt an wat se brauch, an ob eng Organisatioun se därf ausmaachen. Registréiert se do, wou d'Webapplikatioun an den Worker zesummesatzt ginn, sou datt béid averwane sinn. D'Registratur kontrolléiert d'Regelen vun all Plaz, wann se gebaut gëtt an all Kéier, wann Dir `register` urufft, refuséiert en Ensemble, deen ongëlteg wär, mat all Problem, dat genannt gëtt, an hält d'Registratur onverännert. D'Tester vun der Erweiderung selwer sollen feststellen, datt `extensionContractProblems` fir en eidel ass an datt se auszeschalten dat ännert, wat se beaflosst.

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