---
title: "ডেভেলপার নির্দেশিকা"
description: "Quire REST API, OAuth, webhook, MCP server ও extension সম্পর্কে জানুন।"
image: "https://docs.quirelms.com/og.png"
---

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

# ডেভেলপার নির্দেশিকা

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

আপনার প্রতিষ্ঠানের API address ও সীমিত scope-এর credential ব্যবহার করুন। read request দিয়ে শুরু করে response যাচাই করুন; source control ও documentation-এর উদাহরণের বাইরে secret রাখুন।

Quire-এর একটি public API আছে: OpenAPI 3.1 document-এ বর্ণিত HTTPS-এর ওপর REST, event-এর জন্য signed webhook এবং AI assistant-এর জন্য MCP server। [API reference](https://docs.quirelms.com/api/)-এ প্রতিটি endpoint ও event তালিকাভুক্ত।

## ঠিকানা <!--quire:addresses-->

প্রতিটি প্রতিষ্ঠানের নিজস্ব address আছে এবং API তার অধীনে থাকে:

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

Credential-ই প্রতিষ্ঠান নির্ধারণ করে। এক প্রতিষ্ঠানের address-এ অন্য প্রতিষ্ঠানের key ব্যবহার করলে তা প্রত্যাখ্যাত হয়।

যেকোনো প্রতিষ্ঠানের address-এ `/api/v1/openapi.json`-এ OpenAPI document পাওয়া যায়, তাই client generator সবসময় ব্যবহৃত সংস্করণ দেখতে পায়।

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

**API key** script ও server-to-server integration-এর জন্য। প্রশাসক `/admin/integrations/api-keys`-এ একটি তৈরি করে scope বেছে নেন এবং key একবারই দেখতে পান। bearer token হিসেবে পাঠান:

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

Key-এর শুরু `qk_live_` অথবা `qk_test_` দিয়ে। প্রতিটি integration-কে নিজস্ব key দিন।

**OAuth 2.1** sign-in করা ব্যক্তির পরিচয়ে কাজ করা application-এর জন্য। `/admin/integrations/oauth-clients`-এ client নিবন্ধন করে PKCE-সহ authorization code flow (`/oauth/authorize`, `/oauth/token`) অথবা machine client-এর জন্য client credentials ব্যবহার করুন। Discovery-র ঠিকানা `/.well-known/oauth-authorization-server`। Scope token কী করতে পারবে তা সীমিত করে; ব্যক্তির অনুমতির বাইরে কিছু করতে দেয় না।

Scope হলো `resource:read`, `resource:write` ও `resource:delete`; যেমন `courses:read` বা `enrolments:write`। চারটি privileged scope consent screen-এ সতর্কতাসহ দেখানো হয়: `audit:read`, `roles:write`, `tenants:write` এবং `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>

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

- **Pagination**: প্রতিটি list cursor দিয়ে paginated। `limit` দিন; `next_cursor` নিন (`page` object-এ থাকে) এবং `cursor` হিসেবে পাঠান, যতক্ষণ `has_more` true থাকে (নিচে উদাহরণ)। offset নেই।
- **এরপরের পরিবর্তন**: `updated_since` একটি সময়ের পর বদলানো তথ্য দেয়। কী মুছেছে জানতে এটি `include_deleted=true`-এর সঙ্গে ব্যবহার করুন অথবা `/<resource>/deletions` পড়ুন।
- **বাহ্যিক identifier**: বেশিরভাগ resource-এ নিজের `external_id` দেওয়া যায়; `/<resource>/ext:{external_id}` দিয়ে সেটি পড়া বা upsert করা যায়, তাই sync-এ Quire-এর identifier রাখতে হয় না।
- **Idempotency**: `Idempotency-Key` header `POST`, `PATCH` ও `DELETE`-এ পাঠান। একই key দিয়ে retry করলে কাজ দুবার না করে প্রথম response ফেরত দেয়। Bulk endpoint-এ এটি বাধ্যতামূলক।
- **সংস্করণ**: major version path-এ (`/v1`) থাকে। এর মধ্যে breaking change-গুলো তারিখ দেওয়া revision; `Quire-Version` header দিয়ে বেছে নিন, যেমন `Quire-Version: 2026-09-20`। Header না দিলে credential দেওয়ার সময় যে revision বর্তমান ছিল সেটি পাবেন।

একটি list-এর page:

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

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

প্রতিটি error হলো RFC 9457 problem document:

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

`code` দেখে branch করুন, এটি স্থিতিশীল; `detail` মানুষের জন্য লেখা, দেখানো নিরাপদ এবং বদলাতে পারে। code চিনতে না পারলে `category` অনুযায়ী ভাগ করুন:

| Category | Status | Retry |
| --- | --- | --- |
| `validation` | 422, `errors`-এ field-এর বিবরণসহ | না |
| `authentication` | 401 | না |
| `authorization` | 403 | না |
| `not_found` | 404 | না |
| `conflict` | 409 | কখনো |
| `precondition` | 412 | না |
| `quota` | plan-এর জন্য 402, আকারের জন্য 413 | না |
| `rate_limit` | 429, `Retry-After`-সহ | হ্যাঁ |
| `upstream` | 502 বা 504 | হ্যাঁ |
| `internal` | 500 | হ্যাঁ |

Support-এর সঙ্গে যোগাযোগ করলে `request_id` দিন।

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

`/admin/webhooks`-এ অথবা API-র `/webhook_subscriptions` দিয়ে subscribe করুন। event-এর নাম (`enrolment.created`), area (`enrolment.*`) অথবা সব event (`*`) বেছে নিন। Quire প্রথমে `webhook.ping` পাঠায়; endpoint উত্তর দিলে subscription চালু হয়।

Delivery-গুলো Standard Webhooks specification অনুসরণ করে:

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

Delivery যাচাই করতে:

1. JSON parse করার আগে পাওয়া হুবহু byte থেকে `{webhook-id}.{webhook-timestamp}.{raw body}` string তৈরি করুন।
2. subscription secret দিয়ে এর ওপর HMAC-SHA256 হিসাব করে base64 করুন।
3. `v1,`-এর প্রতিটি value-কে `webhook-signature`-এর সঙ্গে constant time-এ তুলনা করুন। Secret rotation চললে দুটি থাকতে পারে; যেকোনো একটির সঙ্গে মিললেই বৈধ।
4. আপনার clock থেকে পাঁচ মিনিটের বেশি ব্যবধান থাকা timestamp প্রত্যাখ্যান করুন।

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

`webhook-id` ধরে duplicate delivery বাদ দিন: একই delivery একাধিকবার আসতে পারে। Body-তে identifier ও সংক্ষিপ্ত সারাংশ থাকে; resource-এর বর্তমান অবস্থা পেতে সেটি fetch করুন। ব্যর্থ delivery-তে সর্বোচ্চ 72 ঘণ্টা backoff দিয়ে retry হয় এবং delivery log থেকে আবার চালানো যায়।

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

Quire-এর MCP server streamable HTTP দিয়ে প্রতিষ্ঠানের address-এ `/mcp`-তে থাকে। MCP client `/.well-known/oauth-protected-resource` থেকে OAuth server খুঁজে পায়; যেকোনো OAuth client-এর মতো ব্যক্তি sign in করে সম্মতি দেন। Tool-গুলো সেই ব্যক্তির অনুমতিতে তাঁর পরিচয়ে কাজ করে, আর ধ্বংসাত্মক tool-এ নিশ্চিতকরণ চাওয়া হয়। `/admin/integrations/mcp`-এ প্রশাসকেরা কোন tool পাওয়া যাবে তা বেছে নেন।

<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 ও API <!--quire:plans-and-the-api-->

API entitlement-এর মধ্যে API key, OAuth client, webhook ও MCP server পড়ে এবং প্রতিটি standard plan-এ এটি থাকে। যে plan-এ এটি নেই, সেখানে key, client বা subscription তৈরি প্রত্যাখ্যাত হয়, REST write ও MCP connection প্রত্যাখ্যাত হয়, তবে data export করা যায় বলে REST read চলে। প্রত্যাখ্যানের problem document-এ `commerce.plan_entitlement` code থাকে, যার category `precondition`।

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

Quire-এর নিজস্ব activity type, block, enrolment method, sign-in method, question type, report, theme ও integration একই extension registry-তে ঘোষণা করা হয়; নিজে host করা install-এ অতিরিক্ত extension যোগ করা যায়। Extension compile করে যুক্ত হয়: runtime plugin loader নেই এবং hosted প্রতিষ্ঠান কোনো extension যোগ করতে পারে না। `/admin/extensions`-এ প্রশাসকেরা প্রতিষ্ঠানের জন্য প্রতিটি extension চালু বা বন্ধ করেন (দেখুন [প্রশাসক নির্দেশিকা](/bn/admin/extensions/))।

Extension লিখতে `packages/integration/extensions/src/sample.ts`-এর sample block ও theme দিয়ে শুরু করুন। Extension point বেছে নিয়ে `points.ts`-এ তার contract পড়ুন, তারপর id, version, licence, কী সরবরাহ ও প্রয়োজন করে এবং প্রতিষ্ঠান এটি বন্ধ করতে পারবে কি না—এসব দিয়ে extension ঘোষণা করুন। Web application ও worker যেখানে একত্র হয় সেখানে নিবন্ধন করুন, যাতে উভয়ের registry মেলে। Build-এর সময় এবং `register` call করলে registry প্রতিটি point-এর নিজস্ব নিয়ম পরীক্ষা করে; কোনো সমস্যাসহ অবৈধ set প্রত্যাখ্যান করে এবং প্রত্যাখ্যান করলে registry অপরিবর্তিত রাখে। Extension-এর test-এ যাচাই করা উচিত `extensionContractProblems` ফাঁকা এবং এটি বন্ধ করলে এর প্রভাবিত বিষয় বদলে যায়।

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