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/coursesLa 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=50Le 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 ilnext_cursordipagecomecursorfinchéhas_moreè true (vedi esempio sotto). Non esiste l’offset. - Modifiche successive:
updated_sincerestituisce ciò che è cambiato dopo un dato orario. Abbinalo ainclude_deleted=trueoppure leggi/<resource>/deletionsper 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-KeyconPOST,PATCHeDELETE. 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’intestazioneQuire-Version, per esempioQuire-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:
- Crea la stringa
{webhook-id}.{webhook-timestamp}.{raw body}dai byte esatti ricevuti, prima di analizzare JSON. - Calcola HMAC-SHA256 usando il segreto della sottoscrizione e codifica il risultato in base64.
- Confrontalo in tempo costante con ogni valore
v1,diwebhook-signature. Durante la rotazione del segreto possono essercene due: basta che corrisponda uno. - 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.