---
title: "Guía para desarrolladores"
description: "La API REST de Quire, OAuth, webhooks, el servidor MCP y las extensiones."
image: "https://docs.quirelms.com/og.png"
---

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

# Guía para desarrolladores

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

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](https://docs.quirelms.com/api/) enumera todos los endpoints y eventos.

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

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 <!--quire:authentication-->

**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`.

<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>

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

- **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 <!--quire:errors-->

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 <!--quire: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 <!--quire: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`.

<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>

## Planes y API <!--quire:plans-and-the-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 <!--quire:extensions-->

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](/es-419/admin/extensions/)).

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.

Source: https://docs.quirelms.com/es-419/developer/index.mdx
