---
title: "Pandhuan pangembang"
description: "REST API Quire, OAuth, webhook, server MCP lan ekstensi."
image: "https://docs.quirelms.com/og.png"
---

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

# Pandhuan pangembang

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

Nggunakake alamat API organisasi panjenengan lan kredensial sing diwatesi scope. Miwiti kanthi panjalukan waca, priksa tanggapane, lan simpen rahasia ing njaba kontrol sumber lan conto dokumentasi.

Quire duwe siji API publik: REST liwat HTTPS, diterangake dening dokumen OpenAPI 3.1, kanthi webhook sing ditandatangani kanggo prastawa lan server MCP kanggo asisten AI. [Referensi API](https://docs.quirelms.com/api/) ndhaftar saben endpoint lan prastawa.

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

Saben organisasi duwe almate dhewe, lan API manggon ing ngisore:

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

Kredensial sing nemtokake organisasi. Kunci kanggo sawijining organisasi sing digunakake ing alamat liyane ditolak.

Dokumen OpenAPI dilayani ing `/api/v1/openapi.json` ing alamat organisasi apa wae, supaya generator klien tansah ndeleng versi sing ditelpon.

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

**Kunci API** kanggo skrip lan integrasi server-ke-server. Administrator nggawe siji ing `/admin/integrations/api-keys`, milih scope-e, lan ndeleng sapisan. Kirim minangka bearer token:

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

Kunci diwiwiti `qk_live_` utawa `qk_test_`. Wenehi saben integrasi kunci dhewe.

**OAuth 2.1** kanggo aplikasi sing tumindak minangka wong sing mlebu. Dhaptarake klien ing `/admin/integrations/oauth-clients`, banjur gunakake alur kode otorisasi nganggo PKCE (`/oauth/authorize`, `/oauth/token`), utawa kredensial klien kanggo klien mesin. Discovery ana ing `/.well-known/oauth-authorization-server`. Scope nyempitake apa sing bisa ditindakake token; ora tau ngidinake ngluwihi sing bisa ditindakake wonge.

Scope iku `resource:read`, `resource:write` lan `resource:delete`, contone `courses:read` utawa `enrolments:write`. Papat iku istimewa lan ditampilake kanthi peringatan ing layar idin: `audit:read`, `roles:write`, `tenants:write` lan `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>

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

- **Paginasi**: saben dhaptar dipaginasi cursor. Kirim `limit`, banjur `next_cursor` saka `page` minangka `cursor` selagi `has_more` bener (conto ing ngisor). Ora ana offset.
- **Owahan wiwit**: `updated_since` mbalekake apa sing owah sawise wektu. Pasangake karo `include_deleted=true`, utawa waca `/<resource>/deletions`, kanggo mangerteni apa sing dibusak.
- **Identifier eksternal**: umume sumber nampa `external_id` dhewe, lan `/<resource>/ext:{external_id}` maca utawa upsert miturute, supaya sinkronisasi ora perlu nyimpen identifier Quire.
- **Idempotensi**: kirim header `Idempotency-Key` ing `POST`, `PATCH` lan `DELETE`. Coba maneh nganggo kunci sing padha mbalekake tanggapan kapisan tinimbang nindakake pagawean kaping pindho. Endpoint bulk mbutuhake.
- **Versi**: versi mayor ana ing path (`/v1`). Ing njerone, saben owahan breaking iku revisi tanggalan, dipilih nganggo header `Quire-Version`, contone `Quire-Version: 2026-09-20`. Tanpa header entuk revisi sing berlaku nalika kredensial diterbitake.

Kaca saka dhaptar:

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

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

Saben kaluputan iku dokumen masalah 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..."}
```

Cabang ing `code`, sing stabil; `detail` ditulis kanggo manungsa, aman ditampilake, lan bisa owah. Nalika ora ngenali kode, kelompokake ing `category`:

| Kategori | Status | Coba maneh |
| --- | --- | --- |
| `validation` | 422, kanthi detail kolom ing `errors` | Ora |
| `authentication` | 401 | Ora |
| `authorization` | 403 | Ora |
| `not_found` | 404 | Ora |
| `conflict` | 409 | Kadhangkala |
| `precondition` | 412 | Ora |
| `quota` | 402 kanggo plan, 413 kanggo ukuran | Ora |
| `rate_limit` | 429, kanthi `Retry-After` | Ya |
| `upstream` | 502 utawa 504 | Ya |
| `internal` | 500 | Ya |

Sebutake `request_id` nalika hubungi dhukungan.

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

Langganan ing `/admin/webhooks`, utawa liwat API ing `/webhook_subscriptions`. Pilih prastawa miturut jeneng (`enrolment.created`), miturut area (`enrolment.*`) utawa kabeh (`*`). Quire dhisik ngirim `webhook.ping`; langganan miwiti yen endpoint panjenengan mangsuli.

Pangiriman ngetutake spesifikasi Standard Webhooks:

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

Kanggo verifikasi pangiriman:

1. Bangun string `{webhook-id}.{webhook-timestamp}.{raw body}` saka byte persis sing ditampa, sadurunge parsing JSON apa wae.
2. Etung HMAC-SHA256 ing ndhuwur nganggo rahasia langganan, lan base64-ake.
3. Bandhingake karo saben nilai `v1,` ing `webhook-signature` ing wektu konstan. Bisa ana loro sajrone rotasi rahasia; sing cocog sah.
4. Tolak timestamp sing luwih saka limang menit saka jam panjenengan.

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

Deduplikasi ing `webhook-id`: pangiriman bisa teka luwih saka sapisan. Bodine nggawa identifier lan ringkesan cendhak; jupuk sumbere kanggo kahanan saiki. Pangiriman gagal dicoba maneh nganggo backoff nganti 72 jam, lan bisa diputer maneh saka log pangiriman.

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

Server MCP Quire ana ing `/mcp` ing alamat organisasi, liwat HTTP sing bisa di-stream. Klien MCP nemokake server OAuth saka `/.well-known/oauth-protected-resource`, lan wonge mlebu lan idin kaya klien OAuth apa wae. Piranti tumindak minangka wong kasebut, karo ijine, lan piranti ngrusak njaluk konfirmasi. Administrator milih piranti apa sing kasedhiya ing `/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>

## Plan lan API <!--quire:plans-and-the-api-->

Kunci API, klien OAuth, webhook lan server MCP kalebu entitlement API plan, lan saben plan standar kalebu. Ing plan tanpa iku, nggawe kunci, klien utawa langganan ditolak, tulisan REST lan sambungan MCP ditolak, lan wacan REST tetep mlaku supaya data tetep bisa diekspor. Penolakan iku dokumen masalah kanthi kode `commerce.plan_entitlement`, ing kategori `precondition`.

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

Jinis aktivitas, blok, cara pendaftaran, cara mlebu, jinis pitakonan, laporan, tema lan integrasi Quire dhewe dideklarasikake liwat registri ekstensi sing padha sing bisa ditambahi instalasi sing dihosting dhewe. Ekstensi dikompilasi ing njero: ora ana loader plugin runtime, lan organisasi sing dihosting ora bisa nambah siji. Administrator nguripake utawa mateni saben ekstensi kanggo organisasine ing `/admin/extensions` (deleng [pandhuan administrator](/jv/admin/extensions/)).

Kanggo nulis siji, miwiti saka blok lan tema conto ing `packages/integration/extensions/src/sample.ts`. Pilih titik ekstensi lan waca kontrakne ing `points.ts`, banjur deklarasikake ekstensi kanthi id, versi, lisensi, apa sing disedhiyakake lan dibutuhake, lan apa organisasi bisa mateni. Dhaptarake ing panggonan aplikasi web lan worker disusun, supaya kalorone sarujuk. Registri mriksa aturan dhewe saben titik nalika dibangun lan saben nelpon `register`, nolak set sing bakal ora sah kanthi saben masalah dijenengi, lan ninggalake registri ora owah nalika nindakake. Tes ekstensi dhewe kudune negesake manawa `extensionContractProblems` kosong lan manawa mateni ngowahi apa sing dipengaruhi.

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