Saltar al contenido

Guía para desarrolladores

La API REST de Quire, OAuth, webhooks, el servidor MCP y las extensiones.

Ver como Markdown

Usa la dirección de API de tu organización y una credencial con permisos limitados. Empieza con una solicitud de lectura, revisa la respuesta y mantén los secretos fuera del control de versiones y de los ejemplos de documentación.

Quire tiene una API pública: REST sobre HTTPS, descrita en un documento OpenAPI 3.1, con webhooks firmados para eventos y un servidor MCP para asistentes de IA. La referencia de la API enumera todos los endpoints y eventos.

Direcciones

Cada organización tiene su propia dirección y la API está disponible en ella:

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

La credencial determina la organización. Se rechaza una clave de una organización si se usa en la dirección de otra.

El documento OpenAPI está disponible en /api/v1/openapi.json desde cualquier dirección de una organización, así que los generadores de clientes siempre ven la versión a la que llamas.

Autenticación

Claves de API: para scripts e integraciones entre servidores. Un administrador crea una en /admin/integrations/api-keys, elige sus permisos y solo puede verla una vez. Envíala como token bearer:

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

Las claves empiezan con qk_live_ o qk_test_. Usa una clave distinta para cada integración.

OAuth 2.1: para apps que actúan como una persona con sesión iniciada. Registra un cliente en /admin/integrations/oauth-clients y luego usa el flujo de código de autorización con PKCE (/oauth/authorize, /oauth/token) o las credenciales de cliente para un cliente de máquina. La detección está en /.well-known/oauth-authorization-server. Un permiso limita lo que puede hacer un token; nunca le permite hacer más de lo que podría hacer la persona.

Los permisos son resource:read, resource:write y resource:delete; por ejemplo, courses:read o enrolments:write. Cuatro son privilegiados y se muestran con una advertencia en la pantalla de consentimiento: audit:read, roles:write, tenants:write y users:delete.

The API keys page with one key, the person it acts as, its scopes and its status, and a form to create another.
API keys list who each key acts as and what it may reach.

Solicitudes

  • Paginación: todas las listas se paginan con cursores. Envía limit y luego pasa next_cursor de page como cursor mientras has_more sea true (como en el ejemplo). No se usa offset.
  • Cambios desde una fecha: updated_since devuelve los cambios posteriores a una hora. Combínalo con include_deleted=true o lee /<resource>/deletions para saber qué se eliminó.
  • Identificadores externos: la mayoría de los recursos aceptan tu propio external_id, y /<resource>/ext:{external_id} permite leer o insertar/actualizar mediante ese valor, para que una sincronización no tenga que almacenar los identificadores de Quire.
  • Idempotencia: envía la cabecera Idempotency-Key en solicitudes POST, PATCH y DELETE. Un reintento con la misma clave devuelve la primera respuesta, en lugar de ejecutar el trabajo dos veces. Los endpoints masivos la requieren.
  • Versiones: la versión principal está en la ruta (/v1). Dentro de esa versión, cada cambio incompatible tiene una revisión fechada, que se elige con la cabecera Quire-Version, por ejemplo, Quire-Version: 2026-09-20. Si no envías la cabecera, recibes la revisión vigente cuando se emitió tu credencial.

Una página de una lista:

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

Errores

Todos los errores son documentos de 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..."}

Usa code, que es estable, para decidir cómo responder; detail está redactado para las personas, es seguro mostrarlo y puede cambiar. Si no reconoces un código, clasifica el error según category:

Categoría Estado Reintento
validation 422, con detalles de campos en errors No
authentication 401 No
authorization 403 No
not_found 404 No
conflict 409 A veces
precondition 412 No
quota 402 para el plan, 413 para el tamaño No
rate_limit 429, con Retry-After Sí
upstream 502 o 504 Sí
internal 500 Sí

Menciona request_id cuando te comuniques con soporte.

Webhooks

Suscríbete en /admin/webhooks o mediante la API en /webhook_subscriptions. Elige eventos por nombre (enrolment.created), por área (enrolment.*) o todos (*). Quire envía primero un webhook.ping; la suscripción comienza cuando tu endpoint responde.

Las entregas siguen la especificación Standard Webhooks:

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

Para verificar una entrega:

  1. Construye la cadena {webhook-id}.{webhook-timestamp}.{raw body} a partir de los bytes exactos que recibiste, antes de analizar el JSON.
  2. Calcula HMAC-SHA256 sobre la cadena con el secreto de tu suscripción y codifica el resultado en base64.
  3. Compara en tiempo constante con cada valor v1, de webhook-signature. Puede haber dos durante la rotación de un secreto; basta con que uno coincida.
  4. Rechaza una marca de tiempo que difiera más de cinco minutos de tu reloj.
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 entregas duplicadas con webhook-id: un evento puede llegar más de una vez. El cuerpo contiene identificadores y un breve resumen; consulta el recurso para obtener su estado actual. Los reintentos de entregas fallidas se espacian progresivamente durante un máximo de 72 horas; puedes volver a enviarlas desde el registro de entregas.

MCP

El servidor MCP de Quire está en /mcp, en la dirección de la organización, y usa HTTP transmisible. Un cliente MCP obtiene el servidor OAuth de /.well-known/oauth-protected-resource; la persona inicia sesión y da su consentimiento igual que con cualquier cliente OAuth. Las herramientas actúan con los permisos de esa persona, y las operaciones destructivas piden confirmación. Los administradores eligen qué herramientas están disponibles en /admin/integrations/mcp.

The AI assistants page with the server address to give an assistant and a table of the tools it can use.
AI assistants (MCP): the server address, and the tools an assistant may call.

Planes y API

Las claves de API, los clientes OAuth, los webhooks y el servidor MCP pertenecen al permiso de API del plan, incluido en todos los planes estándar. Si un plan no lo incluye, se rechazan la creación de claves, clientes y suscripciones, las escrituras REST y las conexiones MCP. Las lecturas REST siguen disponibles para que se puedan exportar los datos. El rechazo es un documento de problema con el código commerce.plan_entitlement, en la categoría precondition.

Extensiones

Los tipos de actividad, bloques, métodos de inscripción y de inicio de sesión, tipos de preguntas, informes, temas e integraciones propios de Quire se declaran en el mismo registro de extensiones que puede ampliar una instalación autoalojada. Las extensiones se compilan en la aplicación: no hay un cargador de complementos en tiempo de ejecución y una organización alojada no puede agregar uno. Los administradores activan o desactivan cada extensión para su organización en /admin/extensions (consulta la guía para administradores).

Para crear una, empieza con el bloque y el tema de ejemplo en packages/integration/extensions/src/sample.ts. Elige el punto de extensión y lee su contrato en points.ts; luego declara la extensión con un ID, una versión, una licencia, lo que ofrece y necesita, y si una organización puede desactivarla. Regístrala donde se integran la aplicación web y el worker para que ambos estén de acuerdo. El registro verifica las reglas de cada punto al crearse y cada vez que llamas a register; rechaza un conjunto no válido e indica todos los problemas, y lo deja sin cambios. Las pruebas de la extensión deben comprobar que extensionContractProblems no detecta problemas en ella y que desactivarla cambia lo que afecta.

Navegación

Escribe para buscar…

↑↓ navegar↵ seleccionarEsc cerrar