---
title: "Entwicklerhandbuch"
description: "Die Quire-REST-API, OAuth, Webhooks, der MCP-Server und Erweiterungen."
image: "https://docs.quirelms.com/og.png"
---

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

# Entwicklerhandbuch

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

Verwenden Sie die API-Adresse Ihrer Organisation und Zugangsdaten mit eingeschränktem Berechtigungsumfang. Beginnen Sie mit einer Leseanfrage, prüfen Sie die Antwort und halten Sie Secrets aus der Versionsverwaltung und aus Dokumentationsbeispielen heraus.

Quire hat eine öffentliche API: REST über HTTPS, beschrieben durch ein OpenAPI-3.1-Dokument, signierte Webhooks für Ereignisse und einen MCP-Server für KI-Assistenten. Die [API-Referenz](https://docs.quirelms.com/api/) führt alle Endpunkte und Ereignisse auf.

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

Jede Organisation hat eine eigene Adresse, und die API befindet sich darunter:

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

Die Zugangsdaten bestimmen die Organisation. Ein Schlüssel für eine Organisation wird an der Adresse einer anderen abgewiesen.

Das OpenAPI-Dokument wird unter `/api/v1/openapi.json` an der Adresse jeder Organisation bereitgestellt. So sehen Client-Generatoren immer die Version, die Sie aufrufen.

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

**API-Schlüssel** sind für Skripte und Server-zu-Server-Integrationen gedacht. Ein Administrator erstellt einen unter `/admin/integrations/api-keys`, wählt dessen Berechtigungsbereiche aus und sieht ihn nur einmal. Senden Sie ihn als Bearer-Token:

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

Schlüssel beginnen mit `qk_live_` oder `qk_test_`. Verwenden Sie für jede Integration einen eigenen Schlüssel.

**OAuth 2.1** ist für Anwendungen gedacht, die im Namen einer angemeldeten Person handeln. Registrieren Sie einen Client unter `/admin/integrations/oauth-clients` und verwenden Sie dann den Authorization-Code-Flow mit PKCE (`/oauth/authorize`, `/oauth/token`) oder Client Credentials für einen Maschinen-Client. Die Discovery-Adresse lautet `/.well-known/oauth-authorization-server`. Ein Scope schränkt ein, was ein Token darf; er kann niemals mehr Rechte verleihen, als die Person selbst hat.

Scopes sind `resource:read`, `resource:write` und `resource:delete`, zum Beispiel `courses:read` oder `enrolments:write`. Vier Scopes haben besondere Rechte und werden auf dem Zustimmungsbildschirm mit einer Warnung angezeigt: `audit:read`, `roles:write`, `tenants:write` und `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>

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

- **Seitennavigation**: Jede Liste ist cursor-paginiert. Übergeben Sie `limit` und anschließend `next_cursor` aus `page` als `cursor`, solange `has_more` den Wert true hat (siehe Beispiel unten). Einen Offset gibt es nicht.
- **Änderungen seit einem Zeitpunkt**: `updated_since` gibt zurück, was sich nach einem Zeitpunkt geändert hat. Kombinieren Sie es mit `include_deleted=true` oder lesen Sie `/<resource>/deletions`, um zu erfahren, was entfernt wurde.
- **Externe Kennungen**: Die meisten Ressourcen akzeptieren Ihre eigene `external_id`. Über `/<resource>/ext:{external_id}` lassen sich Datensätze anhand dieser Kennung abrufen oder per Upsert aktualisieren. Eine Synchronisierung muss daher keine Quire-Kennungen speichern.
- **Idempotenz**: Senden Sie einen `Idempotency-Key`-Header bei `POST`, `PATCH` und `DELETE`. Ein erneuter Versuch mit demselben Schlüssel gibt die erste Antwort zurück, statt die Arbeit doppelt auszuführen. Für Bulk-Endpunkte ist dieser Header erforderlich.
- **Versionen**: Die Hauptversion steht im Pfad (`/v1`). Innerhalb davon wird jede inkompatible Änderung durch eine datierte Revision abgebildet, die über den Header `Quire-Version` ausgewählt wird, zum Beispiel `Quire-Version: 2026-09-20`. Ohne Header erhalten Sie die Revision, die bei Ausstellung Ihrer Zugangsdaten aktuell war.

Eine Seite einer Liste:

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

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

Jeder Fehler ist ein RFC-9457-Problem-Dokument:

```
{"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..."}
```

Verzweigen Sie anhand von `code`, denn dieser Wert bleibt stabil. `detail` ist für Menschen formuliert, kann ihnen gefahrlos angezeigt werden und sich ändern. Wenn Ihnen ein Code unbekannt ist, orientieren Sie sich an `category`:

| Kategorie | Status | Erneuter Versuch |
| --- | --- | --- |
| `validation` | 422, mit Felddetails in `errors` | Nein |
| `authentication` | 401 | Nein |
| `authorization` | 403 | Nein |
| `not_found` | 404 | Nein |
| `conflict` | 409 | Manchmal |
| `precondition` | 412 | Nein |
| `quota` | 402 für den Tarif, 413 für die Größe | Nein |
| `rate_limit` | 429, mit `Retry-After` | Ja |
| `upstream` | 502 oder 504 | Ja |
| `internal` | 500 | Ja |

Nennen Sie `request_id`, wenn Sie den Support kontaktieren.

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

Richten Sie ein Abonnement unter `/admin/webhooks` oder über die API unter `/webhook_subscriptions` ein. Wählen Sie Ereignisse nach Namen (`enrolment.created`), einen Bereich (`enrolment.*`) oder alle Ereignisse (`*`) aus. Quire sendet zuerst ein `webhook.ping`; das Abonnement beginnt, sobald Ihr Endpunkt darauf antwortet.

Zustellungen entsprechen der Standard-Webhooks-Spezifikation:

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

So verifizieren Sie eine Zustellung:

1. Bauen Sie aus den genau empfangenen Bytes und vor jedem JSON-Parsing die Zeichenfolge `{webhook-id}.{webhook-timestamp}.{raw body}`.
2. Berechnen Sie darüber mit dem Abonnement-Secret HMAC-SHA256 und kodieren Sie das Ergebnis mit Base64.
3. Vergleichen Sie jeden `v1,`-Wert in `webhook-signature` in konstanter Zeit. Während einer Secret-Rotation können zwei Werte vorhanden sein; eine Übereinstimmung reicht aus.
4. Lehnen Sie einen Zeitstempel ab, der mehr als fünf Minuten von Ihrer Uhr abweicht.

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

Deduplizieren Sie anhand von `webhook-id`: Eine Zustellung kann mehrfach eintreffen. Der Body enthält Kennungen und eine kurze Zusammenfassung; rufen Sie den Datensatz ab, um seinen aktuellen Zustand zu erhalten. Fehlgeschlagene Zustellungen werden bis zu 72 Stunden lang mit zunehmenden Abständen erneut versucht und können im Zustellungsprotokoll erneut abgespielt werden.

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

Der MCP-Server von Quire ist unter `/mcp` an der Organisationsadresse über streamable HTTP erreichbar. Ein MCP-Client ermittelt den OAuth-Server über `/.well-known/oauth-protected-resource`; die Person meldet sich an und stimmt wie bei jedem OAuth-Client zu. Werkzeuge handeln mit den Berechtigungen dieser Person, und destruktive Werkzeuge verlangen eine Bestätigung. Administratoren legen unter `/admin/integrations/mcp` fest, welche Werkzeuge verfügbar sind.

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

## Tarife und die API <!--quire:plans-and-the-api-->

API-Schlüssel, OAuth-Clients, Webhooks und der MCP-Server gehören zum API-Leistungsumfang des Tarifs, den alle Standardtarife enthalten. In einem Tarif ohne diesen Leistungsumfang wird das Erstellen von Schlüsseln, Clients und Abonnements sowie das Schreiben über REST und der Verbindungsaufbau über MCP abgelehnt. REST-Lesezugriffe funktionieren weiterhin, damit die Daten exportiert werden können. Die Ablehnung ist ein Problem-Dokument mit dem Code `commerce.plan_entitlement` und der Kategorie `precondition`.

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

Die von Quire bereitgestellten Aktivitätstypen, Blöcke, Einschreibungsmethoden, Anmeldemethoden, Fragetypen, Berichte, Themes und Integrationen werden über dieselbe Erweiterungsregistrierung deklariert, zu der eine selbst gehostete Installation weitere Einträge hinzufügen kann. Erweiterungen werden einkompiliert: Es gibt keinen Plugin-Loader zur Laufzeit, und eine gehostete Organisation kann keine Erweiterung hinzufügen. Administratoren schalten Erweiterungen für ihre Organisation unter `/admin/extensions` ein oder aus (siehe [Administratorhandbuch](/de/admin/extensions/)).

Beginnen Sie beim Schreiben einer Erweiterung mit dem Beispiel-Block und dem Beispiel-Theme unter `packages/integration/extensions/src/sample.ts`. Wählen Sie den Erweiterungspunkt und lesen Sie dessen Vertrag in `points.ts`. Deklarieren Sie die Erweiterung anschließend mit ID, Version, Lizenz, bereitgestellten und benötigten Funktionen sowie der Angabe, ob eine Organisation sie ausschalten darf. Registrieren Sie sie dort, wo Webanwendung und Worker zusammengesetzt werden, damit beide dieselbe Konfiguration verwenden. Beim Aufbau und bei jedem Aufruf von `register` prüft die Registrierung die Regeln des jeweiligen Erweiterungspunkts. Ein ungültiger Satz wird mit Angabe jedes Problems abgewiesen; die Registrierung bleibt dabei unverändert. In den Tests der Erweiterung sollte `extensionContractProblems` für sie leer sein und geprüft werden, ob das Ausschalten verändert, was sie beeinflusst.

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