Vai al contenuto

Guida per sviluppatori

API REST di Quire, OAuth, webhook, server MCP ed estensioni.

Usa l’indirizzo API della tua organizzazione e una credenziale con ambiti specifici. Inizia con una richiesta di lettura, controlla la risposta e tieni i segreti fuori dal controllo versione e dagli esempi della documentazione.

Quire dispone di un’API pubblica: REST su HTTPS, descritta da un documento OpenAPI 3.1, con webhook firmati per gli eventi e un server MCP per gli assistenti IA. La documentazione API elenca ogni endpoint ed evento.

Indirizzi

Ogni organizzazione ha un proprio indirizzo e l’API è disponibile al suo interno:

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

La credenziale determina l’organizzazione. Una chiave di un’organizzazione usata all’indirizzo di un’altra viene rifiutata.

Il documento OpenAPI è disponibile in /api/v1/openapi.json all’indirizzo di qualsiasi organizzazione, così i generatori client vedono sempre la versione a cui stai effettuando le chiamate.

Autenticazione

Le chiavi API servono per script e integrazioni tra server. Un amministratore ne crea una in /admin/integrations/api-keys, ne sceglie gli ambiti e la visualizza una sola volta. Inviala come token bearer:

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

Le chiavi iniziano con qk_live_ o qk_test_. Assegna una chiave distinta a ogni integrazione.

OAuth 2.1 serve per applicazioni che agiscono per conto di una persona connessa. Registra un client in /admin/integrations/oauth-clients, poi usa il flusso del codice di autorizzazione con PKCE (/oauth/authorize, /oauth/token) oppure le credenziali client per un client macchina. La discovery è disponibile in /.well-known/oauth-authorization-server. Un ambito restringe ciò che un token può fare; non gli consente mai di fare più di quanto possa fare la persona.

Gli ambiti sono resource:read, resource:write e resource:delete, per esempio courses:read o enrolments:write. Quattro sono privilegiati e vengono segnalati nella schermata di consenso: audit:read, roles:write, tenants:write e users:delete.

Richieste

  • Paginazione: ogni elenco usa cursori. Passa limit, poi il next_cursor di page come cursor finché has_more è true (vedi esempio sotto). Non esiste l’offset.
  • Modifiche successive: updated_since restituisce ciò che è cambiato dopo un dato orario. Abbinalo a include_deleted=true oppure leggi /<resource>/deletions per sapere cosa è stato rimosso.
  • Identificativi esterni: la maggior parte delle risorse accetta il tuo external_id, e /<resource>/ext:{external_id} permette di leggere o aggiornare per identificativo esterno, così una sincronizzazione non deve conservare gli identificativi di Quire.
  • Idempotenza: invia l’intestazione Idempotency-Key con POST, PATCH e DELETE. Un nuovo tentativo con la stessa chiave restituisce la prima risposta invece di ripetere l’operazione. Gli endpoint in blocco la richiedono.
  • Versioni: il numero di versione principale è nel percorso (/v1). Al suo interno, ogni modifica incompatibile è una revisione datata, selezionata con l’intestazione Quire-Version, per esempio Quire-Version: 2026-09-20. Senza intestazione ricevi la revisione corrente al momento del rilascio della credenziale.

Una pagina di un elenco:

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

Errori

Ogni errore è un documento di problema 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..."}

Gestisci gli errori in base a code, che è stabile; detail è scritto per le persone, è sicuro da mostrare e può cambiare. Se non riconosci un codice, usa category:

Categoria Stato Nuovo tentativo
validation 422, con dettagli dei campi in errors No
authentication 401 No
authorization 403 No
not_found 404 No
conflict 409 A volte
precondition 412 No
quota 402 per il piano, 413 per le dimensioni No
rate_limit 429, con Retry-After Sì
upstream 502 o 504 Sì
internal 500 Sì

Quando contatti l’assistenza, comunica request_id.

Webhook

Sottoscrivi gli eventi in /admin/webhooks oppure tramite l’API in /webhook_subscriptions. Scegli gli eventi per nome (enrolment.created), per area (enrolment.*) o tutti (*). Quire invia prima un webhook.ping; la sottoscrizione inizia quando il tuo endpoint risponde.

Le consegne seguono la specifica Standard Webhooks:

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

Per verificare una consegna:

  1. Crea la stringa {webhook-id}.{webhook-timestamp}.{raw body} dai byte esatti ricevuti, prima di analizzare JSON.
  2. Calcola HMAC-SHA256 usando il segreto della sottoscrizione e codifica il risultato in base64.
  3. Confrontalo in tempo costante con ogni valore v1, di webhook-signature. Durante la rotazione del segreto possono essercene due: basta che corrisponda uno.
  4. Rifiuta un timestamp che differisce dal tuo orologio di più di cinque minuti.
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);
  });
}

Evita duplicati usando webhook-id: la stessa consegna può arrivare più volte. Il corpo contiene identificativi e un breve riepilogo; recupera la risorsa per conoscerne lo stato corrente. Le consegne non riuscite vengono ritentate con intervalli crescenti per un massimo di 72 ore e possono essere riprodotte dal log di consegna.

MCP

Il server MCP di Quire è disponibile in /mcp all’indirizzo dell’organizzazione tramite HTTP trasmissibile. Un client MCP individua il server OAuth da /.well-known/oauth-protected-resource, quindi la persona accede e concede il consenso come con qualsiasi client OAuth. Gli strumenti agiscono con i permessi di quella persona; quelli distruttivi chiedono una conferma. Gli amministratori scelgono gli strumenti disponibili in /admin/integrations/mcp.

Piani e API

Le chiavi API, i client OAuth, i webhook e il server MCP fanno parte del diritto all’API incluso in ogni piano standard. Se il piano non lo include, la creazione di chiavi, client o sottoscrizioni viene rifiutata, le scritture REST e le connessioni MCP vengono rifiutate, mentre le letture REST continuano a funzionare per consentire l’esportazione dei dati. Il rifiuto è un documento di problema con codice commerce.plan_entitlement, nella categoria precondition.

Estensioni

Tipi di attività, blocchi, metodi di iscrizione e di accesso, tipi di domande, rapporti, temi e integrazioni di Quire sono dichiarati tramite lo stesso registro delle estensioni che può essere ampliato nelle installazioni autogestite. Le estensioni sono incluse in fase di compilazione: non esiste un caricatore di plugin durante l’esecuzione e le organizzazioni che usano il servizio ospitato non possono aggiungerne. Gli amministratori attivano o disattivano ogni estensione per la propria organizzazione in /admin/extensions (vedi la guida per amministratori).

Per crearne una, parti dall’esempio di blocco e tema in packages/integration/extensions/src/sample.ts. Scegli il punto di estensione e leggine il contratto in points.ts; poi dichiara l’estensione con identificativo, versione, licenza, requisiti, elementi forniti e indicazione che stabilisce se un’organizzazione può disattivarla. Registrala nel punto in cui vengono composti l’applicazione web e il worker, così entrambi concordano. Il registro controlla le regole specifiche del punto durante la creazione e a ogni chiamata a register; rifiuta un insieme non valido indicando ogni problema e lascia invariato il registro. I test dell’estensione dovrebbero verificare che extensionContractProblems non segnali problemi per essa e che disattivarla modifichi ciò che influenza.

Navigazione

Digita per cercare…

↑↓ per spostarti↵ per selezionareEsc per chiudere