# Agentes de IA: conecta tu asistente a Brelio Source: https://docs.brelio.com/es/agents/index Conecta Claude, ChatGPT, Cursor o VS Code a tu clínica con el servidor MCP de Brelio, instala la skill para agentes y mira qué puede hacer un agente hoy. Tu asistente se conecta al servidor MCP de Brelio con tu propia cuenta. No hace falta clave de API ni código. ```text Dirección del servidor MCP theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} https://api.brelio.app/mcp ``` **1. Añade el servidor a tu asistente** Pega la dirección como conector o servidor MCP. Tienes los pasos para Claude, ChatGPT, Cursor, VS Code, Claude Code, Codex y Gemini CLI en [Conecta tu asistente](/es/quickstart/mcp). **2. Inicia sesión con Brelio** El asistente abre Brelio para que inicies sesión y elijas la clínica. Verá lo mismo que ves tú, nunca más. **3. Haz una pregunta** Por ejemplo: «¿Qué servicios ofrecemos y cuánto dura cada uno?». ## Enseña las reglas de Brelio a tu agente Si trabajas con Claude Code, Cursor o Codex, instala la skill de Brelio. Le enseña a confirmar antes de escribir, a pasar `centerId` y a resolver cada código de error. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} npx skills add https://docs.brelio.com/skill.md ``` Más opciones en [Skill para agentes](/es/quickstart/agent-skill). ## Prompts para empezar | Prompt | Herramienta | | - | - | | ¿Qué servicios ofrecemos y cuánto dura cada uno? | `brelio_list_services` | | ¿Qué profesionales trabajan en la clínica y cuáles están activos? | `brelio_list_professionals` | | ¿Qué datos de pacientes pueden usar ahora mismo los asistentes? | `brelio_list_platform_policy` | Tienes más, y un prompt de sistema para recepción, en [Crea un agente con MCP](/es/guides/mcp-agent). ## Qué puede hacer un agente hoy | | | | - | - | | **Consultar** | Centros, servicios, profesionales, conexiones, webhooks y la política de datos de la clínica, con tus permisos. | | **Escribir** | Todavía no por MCP. Reservar, reprogramar o cancelar se hace con la [API REST](/es/guides/book-appointment). | | **Datos de pacientes** | Solo si la clínica activa el [área de datos](/es/concepts/data-areas) de pacientes y aceptas el acceso para esa conexión. | | **Agenda y pacientes** | Las herramientas existen pero aún no aparecen en las conexiones MCP. Consulta las [limitaciones conocidas](/es/changelog#limitaciones-conocidas). | ## En esta sección > [card] [Conecta tu asistente](/es/quickstart/mcp) > > Los pasos para cada asistente y cómo desconectarlo. > [card] [Skill para agentes](/es/quickstart/agent-skill) > > Las reglas de Brelio para Claude Code, Cursor y Codex. > [card] [Crea un agente con MCP](/es/guides/mcp-agent) > > Prompts, cómo se comporta el servidor y cómo llamarlo desde tu código. > [card] [Herramientas MCP](/es/mcp/tools) > > Cada herramienta, con su permiso, su área de datos y su ruta REST. # Cita Source: https://docs.brelio.com/es/api-reference/appointments/appointment /api-reference/openapi.es.json get /appointments/{id} Devuelve una cita: cuándo es, su estado, profesional, servicio, paciente, centro y versión. **Cuándo usarla:** Antes de mover o cancelar una cita, para leer su versión actual; o para comprobar su estado. # Cancelar cita Source: https://docs.brelio.com/es/api-reference/appointments/cancel-appointment /api-reference/openapi.es.json post /appointments/{id}/cancel Cancela una cita puntual. No se puede deshacer desde la API. **Cuándo usarla:** Solo cuando te pidan cancelar esa cita. Léela antes con appointments.get y envía su versión como expectedVersion; si cambió entretanto, la respuesta es 409. Las citas recurrentes no se pueden cancelar aquí. # Crear cita Source: https://docs.brelio.com/es/api-reference/appointments/create-appointment /api-reference/openapi.es.json post /appointments Reserva una cita de un paciente con un profesional para un servicio, con las mismas reglas que la agenda de Brelio. **Cuándo usarla:** Después de elegir una hora con availability.search. Si la hora ya no está libre, la respuesta es 409: vuelve a buscar. # Mover cita Source: https://docs.brelio.com/es/api-reference/appointments/reschedule-appointment /api-reference/openapi.es.json post /appointments/{id}/reschedule Mueve una cita puntual a otra hora, con las mismas reglas que la agenda de Brelio. No admite citas recurrentes. **Cuándo usarla:** Lee antes la cita con appointments.get y envía su versión como expectedVersion; si cambió, recibes 409 VERSION\_CONFLICT. Elige la nueva hora con availability.search; si ya no está libre, 409. # Agenda Source: https://docs.brelio.com/es/api-reference/calendar/agenda /api-reference/openapi.es.json get /agenda Lee la agenda de la clínica: las citas de un intervalo de tiempo, con inicio y fin, estado, profesional, servicio, paciente y centro. **Cuándo usarla:** Para ver qué citas hay en un día o una semana, o las de un profesional o un centro. Como máximo 31 días por llamada; no devuelve notas ni nombres. # Centros Source: https://docs.brelio.com/es/api-reference/organizations/centers /api-reference/openapi.es.json get /centers Lista los centros de la organización. El centro por defecto lleva isDefault. **Cuándo usarla:** Para saber en qué centros trabaja la clínica o para obtener el id de un centro antes de filtrar por él. # Mis claves de API Source: https://docs.brelio.com/es/api-reference/organizations/my-api-keys /api-reference/openapi.es.json get /me/api-keys Lista las claves de API de la persona en esta organización. **Cuándo usarla:** Para que una persona gestione sus propias claves y conexiones. Una clave de organización no puede usarla, y crear o revocar requiere la sesión de la persona. # Mis conexiones Source: https://docs.brelio.com/es/api-reference/organizations/my-connections /api-reference/openapi.es.json get /me/connections Lista las aplicaciones y asistentes que la persona autorizó a actuar por ella. **Cuándo usarla:** Para que una persona gestione sus propias claves y conexiones. Una clave de organización no puede usarla, y crear o revocar requiere la sesión de la persona. # Claves de API de la organización Source: https://docs.brelio.com/es/api-reference/organizations/organization-api-keys /api-reference/openapi.es.json get /api-keys Lista las claves de API de la organización, sin su secreto. **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. # Apps de partners Source: https://docs.brelio.com/es/api-reference/organizations/partner-apps /api-reference/openapi.es.json get /partner-apps Lista las apps de partners (máquina a máquina) de la organización, sin su secreto. **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. # Revocar clave de API Source: https://docs.brelio.com/es/api-reference/organizations/revoke-api-key /api-reference/openapi.es.json post /api-keys/{id}/revoke Revoca una clave de API de la organización. Una clave solo puede revocarse a sí misma; revocar otras requiere la sesión de una persona. No se puede deshacer. **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. # Revocar app de partner Source: https://docs.brelio.com/es/api-reference/organizations/revoke-partner-app /api-reference/openapi.es.json post /partner-apps/{id}/revoke Elimina una app de partner; sus tokens dejan de funcionar. Una aplicación solo puede eliminarse a sí misma; eliminar otras requiere la sesión de una persona. No se puede deshacer. **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. # Archivar paciente Source: https://docs.brelio.com/es/api-reference/patients/archive-patient /api-reference/openapi.es.json post /patients/{id}/archive Archiva un paciente: deja de aparecer en listas y búsquedas, pero se conserva su historial. Se puede deshacer en la web. **Cuándo usarla:** Cuando la clínica pida retirar un paciente. Lee antes el paciente con patients.get y envía su versión como expectedVersion; si cambió, recibes un 409. # Crear paciente Source: https://docs.brelio.com/es/api-reference/patients/create-patient /api-reference/openapi.es.json post /patients Da de alta un paciente nuevo con sus datos administrativos y lo vincula a un centro. **Cuándo usarla:** Cuando el paciente aún no existe: busca antes con patients.list para no crear un duplicado. # Paciente Source: https://docs.brelio.com/es/api-reference/patients/patient /api-reference/openapi.es.json get /patients/{id} Devuelve los datos administrativos de un paciente: nombre, contacto, fecha de nacimiento y estado. **Cuándo usarla:** Cuando ya tienes el id del paciente y necesitas sus datos de contacto o su estado. # Pacientes Source: https://docs.brelio.com/es/api-reference/patients/patients /api-reference/openapi.es.json get /patients Lista los pacientes de la clínica con sus datos administrativos, ordenados por apellido. **Cuándo usarla:** Para encontrar un paciente por nombre, email o teléfono y obtener su id antes de leer su ficha o darle cita. # Actualizar paciente Source: https://docs.brelio.com/es/api-reference/patients/update-patient /api-reference/openapi.es.json patch /patients/{id} Cambia los datos administrativos de un paciente (nombre, contacto, fecha de nacimiento). Solo cambian los campos que envías; null vacía un campo. **Cuándo usarla:** Cuando haya que corregir o completar los datos de un paciente. Lee antes el paciente con patients.get y envía su versión como expectedVersion; si cambió entretanto, la respuesta es 409 y tienes que volver a leerlo. # Cambiar un área de datos Source: https://docs.brelio.com/es/api-reference/platform-policy/change-a-data-area /api-reference/openapi.es.json patch /platform-policy/{id} Desactiva un área de datos para toda la clínica. Activarla solo se hace en Brelio. **Cuándo usarla:** Cuando la persona pide cortar el acceso de las integraciones a un área. # Datos que pueden usar las integraciones Source: https://docs.brelio.com/es/api-reference/platform-policy/data-integrations-may-use /api-reference/openapi.es.json get /platform-policy Qué áreas de datos (agenda, equipo, pacientes, facturación) permite la clínica que usen los asistentes y las integraciones. **Cuándo usarla:** Cuando una operación responde DATA\_AREA\_OFF, para ver qué está desactivado y decírselo a la persona. # Historial de decisiones Source: https://docs.brelio.com/es/api-reference/platform-policy/decision-history /api-reference/openapi.es.json get /platform-policy/decisions Historial de decisiones sobre los datos de la clínica, de la más reciente a la más antigua: quién, cuándo y con qué condiciones. **Cuándo usarla:** Para auditar quién activó o desactivó un área, o quién aceptó datos de pacientes para una conexión. # Permisos de claves y aplicaciones Source: https://docs.brelio.com/es/api-reference/platform-policy/key-and-app-permissions /api-reference/openapi.es.json get /platform-policy/permissions Los permisos que puede llevar una clave o aplicación, agrupados por área de datos, con su riesgo y si tocan pacientes. **Cuándo usarla:** Antes de pedir una clave nueva, para elegir solo los permisos necesarios. # Profesionales Source: https://docs.brelio.com/es/api-reference/professionals/professionals /api-reference/openapi.es.json get /professionals Lista los profesionales del equipo de la clínica con su estado. **Cuándo usarla:** Para saber quién atiende en la clínica o para obtener el id de un profesional antes de consultar su agenda. # Huecos libres Source: https://docs.brelio.com/es/api-reference/scheduling/free-slots /api-reference/openapi.es.json get /availability Busca huecos libres para reservar una cita de un paciente con un profesional para un servicio: hasta 50 horas de inicio, con las mismas reglas que la agenda de Brelio. **Cuándo usarla:** Antes de crear una cita, para ofrecer horas que se puedan reservar sin conflicto. Intervalos de hasta 31 días. # Servicios Source: https://docs.brelio.com/es/api-reference/services/services /api-reference/openapi.es.json get /services Lista los servicios que ofrece la clínica: nombre, duración, estado y precio. **Cuándo usarla:** Para saber qué se puede reservar, cuánto dura o cuánto cuesta, o para obtener el id de un servicio. # Crear webhook Source: https://docs.brelio.com/es/api-reference/webhooks/create-webhook /api-reference/openapi.es.json post /webhooks Registra una URL https que recibe avisos firmados cuando ocurren los eventos elegidos. **Cuándo usarla:** Para que otro sistema (n8n, Zapier, tu propio servidor) se entere de los cambios sin consultar la API cada poco. # Eliminar webhook Source: https://docs.brelio.com/es/api-reference/webhooks/delete-webhook /api-reference/openapi.es.json post /webhooks/{id}/delete Elimina un webhook y su registro de entregas; deja de recibir avisos al momento. **Cuándo usarla:** Cuando un sistema ya no deba recibir los avisos de la clínica. # Reenviar Source: https://docs.brelio.com/es/api-reference/webhooks/redeliver /api-reference/openapi.es.json post /webhooks/{id}/redeliver Vuelve a enviar una entrega ahora, con el mismo webhook-id para que el receptor no la procese dos veces. **Cuándo usarla:** Después de arreglar el servidor que fallaba, en lugar de esperar al siguiente reintento. # Rotar secreto Source: https://docs.brelio.com/es/api-reference/webhooks/rotate-secret /api-reference/openapi.es.json post /webhooks/{id}/rotate-secret Sustituye el secreto de firma de un webhook; el anterior sigue firmando durante 24 horas. **Cuándo usarla:** Si el secreto puede haberse filtrado, o como cambio periódico. # Probar webhook Source: https://docs.brelio.com/es/api-reference/webhooks/test-webhook /api-reference/openapi.es.json post /webhooks/{id}/test Envía un aviso de prueba firmado al webhook, sea cual sea su suscripción. **Cuándo usarla:** Para comprobar que el servidor recibe y verifica los avisos. # Webhook Source: https://docs.brelio.com/es/api-reference/webhooks/webhook /api-reference/openapi.es.json get /webhooks/{id} Muestra un webhook con sus últimas entregas: respuesta, intentos y siguiente reintento. **Cuándo usarla:** Para diagnosticar por qué un sistema no recibe avisos. # Tipos de evento de webhook Source: https://docs.brelio.com/es/api-reference/webhooks/webhook-event-types /api-reference/openapi.es.json get /webhook-event-types Lista los eventos a los que se puede suscribir un webhook. **Cuándo usarla:** Antes de crear un webhook, para elegir sus eventos. # Webhooks Source: https://docs.brelio.com/es/api-reference/webhooks/webhooks /api-reference/openapi.es.json get /webhooks Lista los webhooks de la organización, su estado y desde cuándo fallan. **Cuándo usarla:** Para revisar qué sistemas reciben avisos o para encontrar el id de un webhook. # Autenticación: claves de API y OAuth Source: https://docs.brelio.com/es/authentication Claves de API de la organización, claves personales, apps de partners (M2M) y OAuth de MCP: cómo inicia sesión cada una y qué ve cada una. Cada llamada lleva una credencial en la cabecera `Authorization`. Nadie llama de forma anónima, y cada credencial pertenece a una sola clínica (organización). ```http theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} Authorization: Bearer ``` Brelio nunca emite tokens: todas las credenciales vienen de WorkOS, el proveedor de identidad de Brelio, y Brelio las comprueba en cada petición. ## Elige una credencial | Estás construyendo | Usa | Actúa como | Ve | | - | - | - | - | | Una integración propia de la clínica (n8n, Make, un script interno) | [Clave de API de la organización](#claves-de-api-de-la-organización) | La clínica | Todos los centros, dentro de los permisos de la clave | | Un script para ti | [Clave personal](#claves-personales) | Tú | Solo lo que ves en Brelio, dentro de los permisos de la clave | | Software que vendes a muchas clínicas | [App de partner](#apps-de-partners-m2m) | La clínica que la creó | Todos los centros, dentro de los permisos de la app | | Un asistente de IA (Claude, ChatGPT, Cursor…) | [OAuth de MCP](#oauth-de-mcp) | Tú | Exactamente lo que ves en Brelio | El acceso efectivo siempre es una **intersección**, nunca una suma: * **Credenciales de la organización** (claves de la organización, apps de partners): los permisos marcados en la clave o la app, dentro de lo que permite el plan de la clínica. Nunca heredan el acceso de la persona que las creó. * **Credenciales personales** (claves personales, conexiones MCP): los permisos de la credencial **y** tus permisos y centros actuales en Brelio, leídos en cada petición. Si pierdes un permiso o dejas la clínica, todas las credenciales que actúan por ti lo pierden en ese mismo momento. Además, en ambos casos, las [áreas de datos](/es/concepts/data-areas) de la clínica deciden qué tipos de datos puede usar cualquier integración. ## Claves de API de la organización Para las herramientas propias de la clínica. La clave no depende de ninguna persona, así que sigue funcionando aunque cambie el equipo. **1. Créala** En Brelio: **Configuración › Integraciones › Claves de API › Nueva clave**. Marca solo los permisos que necesita la herramienta. No puedes dar a una clave un permiso que tú no tienes. **2. Cópiala una vez** La clave completa solo se muestra al crearla. Después Brelio la lista por su nombre, sus últimos caracteres, sus permisos y cuándo se usó por última vez. **3. Llama a la API** ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/centers \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` Si la clave va a leer datos de pacientes, el propietario que la crea también acepta el acceso a datos de pacientes para ella (consulta el [consentimiento de datos de pacientes](/es/concepts/data-areas#consentimiento-de-datos-de-pacientes)). **Revoca** una clave en la misma pantalla. Una clave también puede revocarse a sí misma, por ejemplo cuando una herramienta detecta que se ha filtrado: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/api-keys/$KEY_ID/revoke \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` Una clave nunca puede revocar otras claves: eso lo hace una persona con sesión en Brelio (`403 CREDENTIAL_SESSION_REQUIRED`). ## Claves personales Una clave personal actúa como tú. Sirve para tus propios scripts y herramientas de línea de comandos: nunca puede hacer más que tú en Brelio y deja de funcionar si sales de la clínica. Lista tus claves personales con `GET /v1/me/api-keys`, y los asistentes que has conectado con `GET /v1/me/connections`. Crearlas o revocarlas requiere tu propia sesión en Brelio: una credencial no puede crear credenciales. > [!WARNING] > > **Limitación conocida (noviembre de 2026).** Las claves personales y las conexiones MCP reciben `403 OPERATION_FORBIDDEN` en las operaciones cuyo permiso se concede por centro (agenda, huecos libres, citas y pacientes). Hasta que se corrija, usa una clave de la organización para esas operaciones. Consulta las [limitaciones conocidas](/es/changelog#limitaciones-conocidas). ## Apps de partners (M2M) Para software que da servicio a muchas clínicas. Cada clínica crea una app de partner para ti en **Configuración › Integraciones › Apps de partners**, marca sus permisos y te da su client ID y su client secret. Tu servidor los cambia por un token de acceso de corta duración con el grant OAuth 2.0 `client_credentials`, y envía ese token como credencial bearer. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://palatial-engine-44.authkit.app/oauth2/token \ -d grant_type=client_credentials \ -d client_id="$BRELIO_CLIENT_ID" \ -d client_secret="$BRELIO_CLIENT_SECRET" ``` ```json Respuesta (forma) theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "access_token": "eyJhbGciOi…", "token_type": "Bearer", "expires_in": 3600 } ``` ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/professionals \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` * El token lleva la clínica (`org_id`) y los permisos de la app. Brelio rechaza un token emitido a una persona. * Pide un token nuevo antes de que se acabe `expires_in`; no pidas uno por llamada. * La clínica ve la app en **Conexiones** y puede eliminarla cuando quiera; sus tokens dejan de funcionar. Una app solo puede eliminarse a sí misma (`POST /v1/partner-apps/{id}/revoke`). ## OAuth de MCP Los asistentes de IA se conectan a `https://api.brelio.app/mcp` con OAuth 2.1, e inicias sesión con tu cuenta de Brelio. Nunca manejas un token. Consulta [Conecta tu asistente de IA](/es/quickstart/mcp). Para quien desarrolla clientes, el servidor sigue la especificación de autorización de MCP: 1. Una llamada sin autenticar responde `401` con `WWW-Authenticate: Bearer resource_metadata="https://api.brelio.app/.well-known/oauth-protected-resource/mcp"`. 2. Ese documento indica el servidor de autorización; Brelio usa WorkOS AuthKit, que admite Client ID Metadata Documents y registro dinámico de clientes. 3. Pide el token con `resource=https://api.brelio.app/mcp`. La audiencia del token debe ser exactamente esa dirección. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/.well-known/oauth-protected-resource/mcp ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "resource": "https://api.brelio.app/mcp", "authorization_servers": ["https://palatial-engine-44.authkit.app"], "bearer_methods_supported": ["header"], "resource_name": "Brelio", "resource_documentation": "https://docs.brelio.com" } ``` Los tokens de acceso duran cinco minutos y el asistente los renueva. Cuando desconectas un asistente en Brelio, su acceso se corta en cinco minutos como máximo. ## Protege tus credenciales * Guarda las claves y los client secrets en un gestor de secretos o en variables de entorno. Nunca los subas al repositorio, los pegues en un chat ni los pongas en una URL. * Da a cada herramienta su propia clave, solo con los permisos que necesita, para poder revocar una sin tocar las demás. * Las claves se validan con WorkOS y el resultado se guarda hasta 60 segundos, así que una clave revocada puede funcionar hasta un minuto. * Cada llamada queda registrada con la credencial que la hizo. La clínica lo ve en **Configuración › Integraciones › Actividad**. # Novedades de la API y del servidor MCP Source: https://docs.brelio.com/es/changelog Qué ha cambiado en la API de Brelio y en el servidor MCP, versión a versión, y qué se sabe que todavía no funciona: las limitaciones conocidas. `/v1` solo crece añadiendo. Un cambio incompatible sale como una versión nueva de la operación, y la antigua anuncia su final con las cabeceras `Deprecation` y `Sunset`. **Noviembre de 2026** v1, acceso anticipado Primera versión de la API de Brelio y del servidor MCP, abierta a clínicas en acceso anticipado. **API REST** en `https://api.brelio.app/v1`: * Equipo: listar centros, profesionales y servicios. * Agenda: leer la agenda, buscar huecos libres, y leer, reservar, reprogramar y cancelar citas sueltas. * Pacientes: buscar, leer, crear, actualizar y archivar datos administrativos. * Integraciones: listar y revocar claves de API de la organización y apps de partners; tus propias claves y conexiones; webhooks (crear, probar, reenviar, rotar el secreto, eliminar); la política de datos de la clínica y sus decisiones. * Concurrencia optimista con `expectedVersion`, `Idempotency-Key` en cada escritura, paginación por cursor, errores RFC 9457 con códigos estables, cabeceras IETF `RateLimit`. **Servidor MCP** en `https://api.brelio.app/mcp`: OAuth con tu cuenta de Brelio, herramientas de lectura generadas desde el mismo catálogo, aceptación de datos de pacientes con elicitation por URL. **Áreas de datos:** la clínica decide qué datos pueden usar las integraciones; los datos de pacientes están desactivados por defecto y necesitan la aceptación de la clínica y de cada credencial. **Webhooks:** firmas Standard Webhooks, reintentos durante 3 días, rotación del secreto con 24 horas de solapamiento. Tipos de evento: `webhookEndpoint.tested`. ## Limitaciones conocidas A noviembre de 2026: * **Las credenciales personales no pueden usar operaciones por centro.** Las conexiones MCP y las claves personales reciben `403 OPERATION_FORBIDDEN` en la agenda, los huecos libres, las citas y los pacientes, y esas herramientas no aparecen por MCP. Las claves de la organización y las apps de partners no están afectadas. * **MCP es solo de lectura.** Las herramientas de escritura (reservar, reprogramar, cancelar, pacientes, webhooks) aparecen en [Herramientas MCP](/es/mcp/tools) pero todavía no se sirven; usa REST. * **Las áreas de pacientes no se pueden activar en producción** hasta que se publiquen las condiciones de datos de pacientes revisadas (`409 PATIENT_DATA_TERMS_PENDING`). * **Los webhooks solo entregan el evento de prueba.** Los eventos de citas y pacientes vienen después. * **Las citas recurrentes** no se pueden reprogramar ni cancelar por la API. * **No hay modo de pruebas.** Todavía no hay credenciales ni clínicas de prueba; prueba la API en tu propia clínica empezando por operaciones de lectura. * **Historia clínica, notas y facturación** todavía no tienen operaciones. # Concurrencia optimista (expectedVersion) Source: https://docs.brelio.com/es/concepts/concurrency Cada registro de la API de Brelio lleva una versión. Devuélvela como expectedVersion para que dos cambios en el mismo registro nunca se pisen. Una recepcionista edita un paciente en Brelio mientras tu integración actualiza el mismo paciente. Sin una comprobación, la última escritura gana sin avisar. Brelio lo evita: cada registro tiene una `version`, y cada cambio dice en qué versión se basa. ## La regla 1. **Lee** el registro. Su `version` viene en la respuesta. 2. **Envía** ese valor sin tocarlo como `expectedVersion` en el cambio. 3. Si alguien cambió el registro entretanto, Brelio responde `409` y no cambia nada. Vuelve a leerlo, decide otra vez y reintenta. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} # 1. Leer curl https://api.brelio.app/v1/patients/$PATIENT_ID \ -H "Authorization: Bearer $BRELIO_API_KEY" # → { "id": "…", "phone": "+34 600 123 456", "version": "2026-11-09 17:59:58.817+00", … } # 2. Cambiar, a partir de esa versión curl -X PATCH https://api.brelio.app/v1/patients/$PATIENT_ID \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": "2026-11-09 17:59:58.817+00", "phone": "+34 600 765 432"}' # → 200, con la nueva "version" ``` Enviar otra vez la versión antigua responde: ```json 409 VERSION_CONFLICT theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/version-conflict", "title": "Request refused", "status": 409, "code": "VERSION_CONFLICT", "detail": "Patient changed since it was read: d4ddb883-ee85-46d1-9719-5e0f7e585392", "request_id": "31adfde0-8936-43e6-9330-4abc092d6d20", "params": { "id": "d4ddb883-ee85-46d1-9719-5e0f7e585392" } } ``` ## Trata la versión como opaca Copia `version` exactamente como la leíste. No la construyas, no la interpretes, no la compares ni la incrementes. Su forma depende del recurso y puede cambiar: | Recurso | `version` tiene esta forma | Tipo de `expectedVersion` | | - | - | - | | Paciente | `"2026-11-09 17:59:58.817+00"` | string | | Cita | `"2"` | string | | Webhook | `2` | entero | | Área de datos (`/v1/platform-policy`) | `0` | entero | Envíala con el tipo que te dio el recurso: string donde `version` es un string, número donde es un número. ## Operaciones que la necesitan | Operación | Código de conflicto | | - | - | | `PATCH /v1/patients/{id}`, `POST /v1/patients/{id}/archive` | `VERSION_CONFLICT` | | `POST /v1/appointments/{id}/reschedule`, `POST /v1/appointments/{id}/cancel` | `VERSION_CONFLICT` | | `POST /v1/webhooks/{id}/rotate-secret`, `POST /v1/webhooks/{id}/delete` | `WEBHOOK_VERSION_CONFLICT` | | `PATCH /v1/platform-policy/{id}` | `DATA_POLICY_VERSION_CONFLICT` | Las creaciones no la llevan: no hay nada que pisar. ## Qué hacer con un 409 No repitas la misma petición: volverá a fallar. Lee el registro, comprueba si tu cambio sigue teniendo sentido con los datos nuevos (puede que la cita ya esté cancelada) y envía una petición nueva con la nueva versión y una `Idempotency-Key` nueva. ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} const BASE = 'https://api.brelio.app/v1' const AUTH = { Authorization: `Bearer ${process.env.BRELIO_API_KEY}` } async function updatePhone(patientId, phone) { for (let attempt = 0; attempt < 3; attempt++) { const patient = await (await fetch(`${BASE}/patients/${patientId}`, { headers: AUTH })).json() const response = await fetch(`${BASE}/patients/${patientId}`, { method: 'PATCH', headers: { ...AUTH, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() }, body: JSON.stringify({ expectedVersion: patient.version, phone }), }) if (response.status !== 409) return response.json() } throw new Error('The patient keeps changing; try later') } ``` # Áreas de datos y consentimiento Source: https://docs.brelio.com/es/concepts/data-areas Cómo decide una clínica qué datos pueden usar las integraciones y los asistentes de IA, y cómo se aceptan los datos de pacientes. Una clínica decide, por área de datos, qué pueden usar sus integraciones y asistentes. Cada operación pertenece a una sola área y solo se ejecuta mientras esa área está activada. ## Las áreas | Id del área | Nombre en Brelio | Contiene | Por defecto | | - | - | - | - | | `agenda` | Agenda y citas | Citas, huecos libres, la agenda de cada profesional | Activada | | `team` | Servicios y equipo | Centros, profesionales, servicios | Activada | | `patients` | Pacientes: datos administrativos | Nombre, datos de contacto, datos de la ficha | **Desactivada** | | `clinical` | Pacientes: historia clínica y notas | Sesiones, notas, documentos clínicos | **Desactivada** | | `billing` | Facturación | Facturas, pagos, bonos | **Desactivada** | | `integrations` | — | Claves, apps, webhooks y estos mismos ajustes | Siempre activada | `integrations` no se puede desactivar: contiene los controles que cortan el acceso. Ninguna operación usa todavía `clinical` ni `billing`. Un propietario cambia las áreas en **Configuración › Integraciones › Resumen**. El cambio se aplica a la siguiente petición. ## Los datos de pacientes necesitan dos decisiones Una operación sobre datos de pacientes (`patients`, `clinical`) solo se ejecuta si se cumplen las dos: 1. **La clínica activó el área.** Un propietario acepta las condiciones de datos de pacientes (tienen versión) y la decisión queda registrada con quién y cuándo. 2. **La credencial está aceptada.** Cada credencial necesita su propia aceptación antes de tocar datos de pacientes: * una conexión MCP o una clave personal: la acepta su persona, la primera vez que hace falta; * una clave de la organización o una app de partner: la acepta el propietario que la crea. > [!NOTE] > > Hasta que se publiquen las condiciones de datos de pacientes revisadas, las áreas de pacientes no se pueden activar en producción (`409 PATIENT_DATA_TERMS_PENDING`), así que todas las operaciones de pacientes responden `403 DATA_AREA_OFF`. ## Qué ves cuando un área está desactivada **Las operaciones de esa área** se rechazan: ```json 403 DATA_AREA_OFF theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/data-area-off", "title": "Request refused", "status": 403, "code": "DATA_AREA_OFF", "detail": "patients.list uses the patients data area («Patients: administrative data»), which this organization does not allow integrations to use. An owner can turn it on in Brelio's integration settings.", "request_id": "…", "params": { "operationId": "patients.list", "area": "patients" }, "hint": "An owner can turn the area on in Brelio; until then, use operations of other areas." } ``` **Los campos de pacientes dentro de otras áreas** llegan como `null`. Por ejemplo, `GET /v1/agenda` sigue listando citas, pero `patientId` es `null` salvo que el área `patients` esté activada y la credencial esté aceptada. Tu código debe aceptar `null` ahí. **Los webhooks** siguen la misma regla en el momento de la entrega: los eventos sobre pacientes no se entregan, y los campos de pacientes de otros eventos llegan como `null`. ## Consentimiento de datos de pacientes Si el área está activada pero la credencial todavía no está aceptada, la llamada responde: ```json 403 PATIENT_DATA_CONSENT_REQUIRED theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/patient-data-consent-required", "title": "Request refused", "status": 403, "code": "PATIENT_DATA_CONSENT_REQUIRED", "detail": "patients.list returns patient data, and nobody has accepted patient data for this credential yet. Its person (or the owner who created it) accepts it in Brelio.", "request_id": "6731564c-80c0-487c-bbf0-e7041f66d19e", "params": { "operationId": "patients.list" }, "hint": "The credential's person accepts patient data in Brelio; organization keys and apps are accepted by an owner." } ``` Por MCP, el mismo rechazo lleva un enlace en `params.consent_url` (`https://brelio.app//conectar/`). Los clientes que admiten elicitation por URL lo abren a la persona; los demás lo muestran. Después de aceptar, repite la llamada. Las operaciones sin datos de pacientes funcionan antes de cualquier aceptación. ## Lee la política de la clínica Una integración con **Gestionar integraciones** puede leer el estado actual: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/platform-policy \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ { "id": "agenda", "label": "Schedule and appointments", "description": "Appointments, free slots and each professional's schedule.", "patientData": false, "enabled": true, "version": 0, "updatedAt": null }, { "id": "team", "label": "Services and team", "description": "Centers, professionals and services.", "patientData": false, "enabled": true, "version": 0, "updatedAt": null }, { "id": "patients", "label": "Patients: administrative data", "description": "Name, contact details and record data.", "patientData": true, "enabled": false, "version": 0, "updatedAt": null }, { "id": "clinical", "label": "Patients: clinical history and notes", "description": "Sessions, notes and clinical documents.", "patientData": true, "enabled": false, "version": 0, "updatedAt": null }, { "id": "billing", "label": "Billing", "description": "Invoices, payments and packages.", "patientData": false, "enabled": false, "version": 0, "updatedAt": null } ], "terms": { "id": "patient-data-terms", "version": 1, "reviewed": false, "available": false } } ``` `GET /v1/platform-policy/decisions` lista todas las decisiones (quién activó o desactivó un área, quién aceptó datos de pacientes para qué credencial), de la más reciente a la más antigua. ## Una integración solo puede cortar acceso Una integración puede **desactivar** un área, por ejemplo para cortar su propio acceso al momento: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X PATCH https://api.brelio.app/v1/platform-policy/agenda \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"enabled": false, "expectedVersion": 0}' ``` **Activar** un área siempre lo hace una persona en Brelio (`403 DATA_AREA_ENABLE_IN_BRELIO`). ## Techo de volumen Una credencial puede recibir como máximo 2.000 fichas de pacientes por hora. A partir de ahí las llamadas responden `429 RATE_LIMITED` y se avisa a los propietarios de la clínica. Consulta los [límites de uso](/es/concepts/rate-limits). # Errores de la API: códigos y soluciones Source: https://docs.brelio.com/es/concepts/errors Cada error es un objeto problem de la RFC 9457 con un código estable. Todos los códigos, qué significan y cómo resolverlos. Los errores responden con `Content-Type: application/problem+json` y siempre con la misma forma. Por MCP, el mismo objeto llega como resultado de la herramienta con `isError: true`. ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/operation-forbidden", "title": "Forbidden", "status": 403, "code": "OPERATION_FORBIDDEN", "detail": "The credential does not grant patients.administrative.read.", "request_id": "4cbc47b2-a8f9-40e4-b0b9-d8d16c099888", "params": { "permission": "patients.administrative.read" }, "hint": "Give the credential the patients.administrative.read permission, or call with one that has it." } ``` | Campo | Significado | | - | - | | `code` | Identificador estable. **Decide con él**, nunca con `detail` ni `title`. | | `status` | El estado HTTP, repetido. | | `detail` | Qué ha pasado, en inglés, para logs y desarrolladores. Puede cambiar. | | `hint` | Qué hacer, cuando hay algo que hacer. | | `params` | Detalles para máquinas: el permiso que falta, los campos no válidos, el id. Nunca datos de la petición. Siempre presente. | | `request_id` | También en la cabecera `x-request-id`. Indícalo cuando pidas ayuda a Brelio. | | `retryAfterSeconds` | En `429` y `503`: segundos de espera. También en `Retry-After`. | La web de Brelio muestra estos códigos en español a la clínica, así que el mismo error se lee igual en todas partes. ## Entrada no válida `422 OPERATION_INPUT_INVALID` lista cada problema por ruta y código, sin repetir los valores (pueden ser datos de pacientes): ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "status": 422, "code": "OPERATION_INPUT_INVALID", "detail": "The input does not match the operation.", "params": { "issues": [ { "path": "patientId", "code": "invalid_format" }, { "path": "start", "code": "invalid_type" } ] } } ``` ## Reintentar o no | Estado | ¿Reintentar? | | - | - | | `400`, `401`, `402`, `403`, `404`, `422` | No. Corrige antes la petición, la credencial o los ajustes de la clínica. | | `409` | No tal cual. Vuelve a leer el registro (conflictos de versión), espera (idempotencia en curso) o cambia la petición. | | `429`, `503` | Sí, tras `Retry-After` segundos, con la misma `Idempotency-Key`. | | `500` | Sí, con espera creciente y la misma `Idempotency-Key`. | ## Todos los códigos Estos son los códigos que hoy devuelven la plataforma y sus operaciones. Una operación también puede dejar pasar un código de las reglas propias de Brelio (por ejemplo, un límite del plan), siempre con la misma forma y un `detail` que lo explica: trata un código desconocido por su `status`. ### Autenticación y acceso | Código | Estado | Significado | Solución | | - | - | - | - | | `PLATFORM_UNAUTHENTICATED` | 401 | No hay credencial bearer, o no es válida, está revocada o ha caducado. | Envía `Authorization: Bearer ` con una clave o un token válidos. | | `PLATFORM_AUTH_UNAVAILABLE` | 503 | No se ha podido comprobar la credencial ahora. | Reintenta tras `Retry-After` (30 s). | | `PLATFORM_ORGANIZATION_NOT_LINKED` | 403 | La credencial pertenece a una organización o persona sin acceso a Brelio. | Usa una credencial creada en una clínica de Brelio a la que perteneces. | | `PLATFORM_ORGANIZATION_AMBIGUOUS` | 403 | Solo MCP: perteneces a varias clínicas y la conexión no indica ninguna. | Vuelve a conectar y elige la clínica. | | `PLATFORM_PLAN_REQUIRED` | 402 | El plan de la clínica no incluye el acceso a la API. | Un propietario lo añade en **Configuración › Suscripción**. | | `PLATFORM_PLAN_UNAVAILABLE` | 503 | No se ha podido confirmar ahora el acceso a la API. | Reintenta en 30 s. | | `OPERATION_FORBIDDEN` | 403 | A la credencial, o a la persona que hay detrás, le falta el permiso de `params.permission`. | Usa una credencial con ese permiso. Consulta los [permisos](/es/concepts/permissions). | | `CENTER_CONTEXT_FORBIDDEN` | 403 | El `centerId` enviado no es uno de los centros de quien llama. | Envía un centro que puedas ver, u omítelo. | | `DATA_AREA_OFF` | 403 | La clínica no deja que las integraciones usen el área de datos de la operación (`params.area`). | Un propietario activa el área en Brelio. Consulta las [áreas de datos](/es/concepts/data-areas). | | `DATA_AREA_UNKNOWN` | 403 | La operación no tiene área de datos, así que ninguna integración puede ejecutarla. | Comunícalo a Brelio. | | `PATIENT_DATA_CONSENT_REQUIRED` | 403 | No se han aceptado los datos de pacientes para esta credencial. Por MCP, `params.consent_url` enlaza a la página de aceptación. | La persona (o el propietario, en claves de la organización y apps) lo acepta en Brelio; luego reintenta. | | `PLATFORM_REVEAL_FORBIDDEN` | 403 | La operación devuelve un secreto de un solo uso, que MCP no puede entregar a una persona. | Llámala por REST, o crea la credencial en Brelio. | ### Peticiones | Código | Estado | Significado | Solución | | - | - | - | - | | `PLATFORM_ROUTE_NOT_FOUND` | 404 | No hay ninguna operación en ese método y ruta. | Consulta `GET /v1/openapi.json`. | | `MALFORMED_JSON_BODY` | 400 | El cuerpo no es un objeto JSON. | Envía un objeto JSON con `Content-Type: application/json`. | | `OPERATION_INPUT_INVALID` | 422 | La entrada no encaja con la operación; mira `params.issues`. | Corrige los campos indicados. | | `IDEMPOTENCY_KEY_REQUIRED` | 400 | Una escritura sin una `Idempotency-Key` válida. | Envía un UUID. Consulta la [idempotencia](/es/concepts/idempotency). | | `OPERATION_IDEMPOTENCY_CONFLICT` | 409 | La clave ya se usó con otra petición. | Usa una clave nueva para un cambio nuevo. | | `OPERATION_IDEMPOTENCY_IN_PROGRESS` | 409 | La primera petición con esta clave sigue en curso. | Espera y reintenta con la misma clave. | | `RATE_LIMITED` | 429 | Se ha alcanzado un [límite de uso](/es/concepts/rate-limits). | Espera `Retry-After` segundos. | | `PLATFORM_LIMITS_UNAVAILABLE` | 503 | El limitador no ha respondido; las operaciones con datos de pacientes se cierran. | Reintenta en 5 s. | | `PLATFORM_INTERNAL` | 500 | Solo MCP: un fallo inesperado. No se envía nada sobre él. | Reintenta más tarde; indica el `request_id`. | Una llamada REST que falla de forma inesperada responde `500` con la cabecera `x-request-id`. ### Versiones | Código | Estado | Significado | Solución | | - | - | - | - | | `VERSION_CONFLICT` | 409 | El paciente o la cita cambió desde que lo leíste. | Vuelve a leerlo y reintenta con su nueva `version`. Consulta la [concurrencia](/es/concepts/concurrency). | ### Citas y huecos libres | Código | Estado | Significado | Solución | | - | - | - | - | | `APPOINTMENT_NOT_FOUND` | 404 | No hay ninguna cita con ese id que puedas ver. | Toma los ids de `GET /v1/agenda` o de la respuesta de creación. | | `APPOINTMENT_REFERENCE_NOT_FOUND` | 404 | El paciente, profesional o servicio (`params.what`) no existe o no puedes reservarlo. Los pacientes inactivos no se pueden reservar. | Comprueba el id con la lista correspondiente. | | `APPOINTMENT_CENTER_REQUIRED` | 422 | La clínica tiene varios centros y la cita necesita uno. | Envía `centerId`. | | `APPOINTMENT_OVERLAP_DETECTED` | 409 | La hora ya no está libre para el profesional. | Vuelve a buscar huecos y elige otro. | | `APPOINTMENT_PROFESSIONAL_UNAVAILABLE` | 422 | El profesional no trabaja a esa hora. | Usa un hueco de `GET /v1/availability`. | | `APPOINTMENT_RECURRING_UNSUPPORTED` | 422 | Las citas recurrentes todavía no se pueden cambiar por la API. | Cámbiala en Brelio. | | `APPOINTMENT_CANCEL_IN_BRELIO` | 409 | La cita ya tiene sesión (paciente llegado o después), así que se cancela en Brelio. | Cancélala en Brelio. | | `APPOINTMENT_RESCHEDULE_NOT_ALLOWED` | 409 | El estado de la cita no permite moverla. | Léela; solo se mueven las citas programadas. | | `APPOINTMENT_INVALID_STATE_TRANSITION` | 409 | El cambio no está permitido desde el estado actual de la cita. | Léela y revisa su `status`. | | `AVAILABILITY_CENTER_REQUIRED` | 422 | La clínica tiene varios centros. | Envía `centerId`. | | `AVAILABILITY_REFERENCE_NOT_FOUND` | 404 | El paciente, profesional o servicio (`params.what`) no se puede reservar aquí. | Comprueba el id. | ### Pacientes | Código | Estado | Significado | Solución | | - | - | - | - | | `PATIENT_NOT_FOUND` | 404 | No hay ningún paciente con ese id que puedas ver. Los archivados no se encuentran. | Busca con `GET /v1/patients`. | | `PATIENT_ACTION_NOT_ALLOWED` | 409 | El estado del paciente no permite el cambio (`params.action`: `edit` o `archive`). | Lee el paciente; si hace falta, cámbialo en Brelio. | ### Credenciales | Código | Estado | Significado | Solución | | - | - | - | - | | `CREDENTIAL_NOT_FOUND` | 404 | No hay ninguna clave, app o conexión con ese id en la clínica. | Lístalas primero. | | `CREDENTIAL_SESSION_REQUIRED` | 403 | Crear credenciales o revocar otra necesita una persona en Brelio. | Hazlo en Brelio; una credencial solo puede revocarse a sí misma. | | `CREDENTIAL_PERMISSION_NOT_HELD` | 403 | Has intentado dar permisos que no tienes (`params.permissions`). | Pídeselo a alguien que los tenga. | | `PLATFORM_CREDENTIALS_UNAVAILABLE` | 503 | El proveedor de identidad no ha respondido. | Reintenta más tarde. | | `PLATFORM_CREDENTIALS_REFUSED` | 422 | El proveedor de identidad ha rechazado la petición. | Revisa los nombres y los permisos enviados. | ### Política de datos | Código | Estado | Significado | Solución | | - | - | - | - | | `DATA_POLICY_VERSION_CONFLICT` | 409 | El área cambió desde que la leíste. | Lee `GET /v1/platform-policy` y reenvía con su `version`. | | `DATA_POLICY_UNCHANGED` | 409 | El área ya tiene ese estado. | No hay nada que hacer. | | `DATA_POLICY_NEEDS_PERSON` | 403 | El cambio debe quedar registrado como decisión de una persona. | Usa una credencial personal o Brelio. | | `DATA_AREA_ENABLE_IN_BRELIO` | 403 | Las integraciones solo pueden desactivar áreas. | Un propietario la activa en Brelio. | | `PATIENT_DATA_ACCEPT_IN_BRELIO` | 403 | Los datos de pacientes los acepta una persona en Brelio. | Acéptalos en Brelio. | | `PATIENT_DATA_CREDENTIAL_NOT_YOURS` | 403 | Solo puedes decidir sobre tus propias claves y conexiones. | Pídeselo a un propietario. | | `PATIENT_DATA_NOTHING_TO_REVOKE` | 409 | No hay ninguna aceptación que revocar. | No hay nada que hacer. | | `PATIENT_DATA_TERMS_NOT_ACCEPTED` | 422 | Activar un área de pacientes necesita `acceptTerms: true` y el `termsVersion` leído. | Envía los dos. | | `PATIENT_DATA_TERMS_OUTDATED` | 409 | Las condiciones cambiaron desde que se mostraron. | Muestra las condiciones actuales y envía su versión. | | `PATIENT_DATA_TERMS_PENDING` | 409 | Las condiciones revisadas todavía no están publicadas. | Espera a que se publiquen. | ### Webhooks | Código | Estado | Significado | Solución | | - | - | - | - | | `WEBHOOK_NOT_FOUND` | 404 | No hay ningún webhook con ese id. | Revisa `GET /v1/webhooks`. | | `WEBHOOK_VERSION_CONFLICT` | 409 | El webhook cambió desde que lo leíste. | Vuelve a leerlo y reintenta con su `version`. | | `WEBHOOK_EVENT_TYPE_UNKNOWN` | 422 | Un tipo de evento de `eventTypes` no existe. | Elige entre los de `GET /v1/webhook-event-types`. | | `WEBHOOK_DELIVERY_PENDING` | 409 | Esa entrega ya está en cola. | Espera; `GET /v1/webhooks/{id}` indica cuándo se intenta. | | `WEBHOOK_TEST_RATE_LIMITED` | 429 | Más de 5 pruebas en un minuto para este webhook. | Espera un minuto. | | `WEBHOOK_NOTHING_TO_DO` | 404 | El webhook ya no existe o ya está desactivado. | No hay nada que hacer. | # Claves de idempotencia (Idempotency-Key) Source: https://docs.brelio.com/es/concepts/idempotency Envía una cabecera Idempotency-Key en cada escritura de la API de Brelio para que reintentar una petición nunca reserve ni cree nada dos veces. Un error de red justo después de enviar `POST /v1/appointments` te deja sin saber si la cita existe. Con una clave de idempotencia basta con enviar otra vez la misma petición: Brelio devuelve el primer resultado en lugar de reservar dos veces. ## La regla Cada escritura (`POST` y `PATCH`) necesita la cabecera `Idempotency-Key`. Las lecturas (`GET`) no. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/appointments \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 0f7e3c52-3b1a-4d55-9a7e-6f2b1c8d9e10" \ -d '{"patientId":"…","professionalId":"…","serviceId":"…","start":"2026-11-16T08:00:00Z"}' ``` * **Formato:** de 8 a 255 caracteres ASCII imprimibles, sin espacios. Un UUID v4 es lo más sencillo. * **Una clave por cambio.** Genérala antes del primer intento y mantenla en todos los reintentos de ese mismo cambio. * **Alcance:** por credencial y operación. Dos credenciales nunca chocan. * **Duración:** Brelio recuerda una clave durante 24 horas. ## Qué pasa | Envías | Brelio responde | | - | - | | Una clave nueva | Ejecuta la operación y guarda el resultado. | | La misma clave y el mismo cuerpo otra vez | El resultado guardado, con el mismo estado (por ejemplo `201`), sin ejecutar nada. | | La misma clave con otro cuerpo | `409 OPERATION_IDEMPOTENCY_CONFLICT`. No se ejecuta nada. | | La misma clave mientras la primera petición sigue en curso | `409 OPERATION_IDEMPOTENCY_IN_PROGRESS`. Espera un momento y reintenta. | | Ninguna clave en una escritura | `400 IDEMPOTENCY_KEY_REQUIRED`. | ```json 409 OPERATION_IDEMPOTENCY_CONFLICT theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/operation-idempotency-conflict", "title": "Request refused", "status": 409, "code": "OPERATION_IDEMPOTENCY_CONFLICT", "detail": "The idempotency key was already used with a different request for appointments.create", "request_id": "17eb0fd0-c750-4dfa-bf6a-654f32dbb877", "params": { "operationId": "appointments.create" } } ``` ## Los secretos se muestran una vez Algunas escrituras devuelven un secreto una sola vez (el `secret` de firma de un webhook). Repetir esa petición devuelve el mismo registro **sin** el secreto y con la cabecera `Brelio-Secret-Already-Shown: true`. Guarda el secreto de la primera respuesta. ## Reintenta con seguridad ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} async function createWithRetry(path, body) { const key = crypto.randomUUID() // una vez, antes del primer intento for (let attempt = 0; attempt < 4; attempt++) { try { const response = await fetch(`https://api.brelio.app/v1${path}`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.BRELIO_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': key, }, body: JSON.stringify(body), }) if (response.status < 500 && response.status !== 429) return response } catch { // error de red: reintenta con la misma clave } await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000)) } throw new Error('Brelio did not answer') } ``` # Paginación y filtros de la API Source: https://docs.brelio.com/es/concepts/pagination Todas las listas se paginan con un cursor opaco: devuelve next_cursor como cursor mientras has_more sea true. Todas las listas responden con la misma forma: ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ … ], "next_cursor": "eyJvIjoyfQ", "has_more": true } ``` | Parámetro | Significado | | - | - | | `limit` | Tamaño de página, de 1 a 100. Por defecto 25. | | `cursor` | El `next_cursor` de la página anterior. Omítelo en la primera página. | `next_cursor` es `null` en la última página. El cursor es opaco: no lo construyas ni lo decodifiques. Un cursor que Brelio no emitió responde `422 OPERATION_INPUT_INVALID` con `params.issues[0].path` = `"cursor"`. ## Lee todas las páginas ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl "https://api.brelio.app/v1/patients?limit=2" \ -H "Authorization: Bearer $BRELIO_API_KEY" # → { "data": [ …2 pacientes… ], "next_cursor": "eyJvIjoyfQ", "has_more": true } curl "https://api.brelio.app/v1/patients?limit=2&cursor=eyJvIjoyfQ" \ -H "Authorization: Bearer $BRELIO_API_KEY" # → { "data": [ …1 paciente… ], "next_cursor": null, "has_more": false } ``` ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} async function* listAll(path, params = {}) { let cursor do { const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) }) const response = await fetch(`https://api.brelio.app/v1${path}?${query}`, { headers: { Authorization: `Bearer ${process.env.BRELIO_API_KEY}` }, }) if (!response.ok) throw new Error(JSON.stringify(await response.json())) const page = await response.json() yield* page.data cursor = page.has_more ? page.next_cursor : undefined } while (cursor) } for await (const professional of listAll('/professionals', { status: 'active' })) { console.log(professional.firstName, professional.lastName) } ``` ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import os import requests def list_all(path, **params): cursor = None while True: query = {**params, "limit": 100, **({"cursor": cursor} if cursor else {})} response = requests.get( f"https://api.brelio.app/v1{path}", params=query, headers={"Authorization": f"Bearer {os.environ['BRELIO_API_KEY']}"}, timeout=10, ) response.raise_for_status() page = response.json() yield from page["data"] if not page["has_more"]: return cursor = page["next_cursor"] for professional in list_all("/professionals", status="active"): print(professional["firstName"], professional["lastName"]) ``` ## Filtros Los filtros son parámetros de consulta. Aceptan los mismos nombres de campo y valores que las tablas de la web de Brelio. Cada lista tiene los suyos en la [referencia de la API](/es/reference/operations): | Lista | Filtros | | - | - | | `GET /v1/patients` | `search` (nombre, email, teléfono, código de paciente, código interno o número de identificación, como el buscador de la web), `status` (`active`, `inactive`, `discharged`) | | `GET /v1/professionals` | `status` (`pending`, `active`, `inactive`) | | `GET /v1/services` | `search`, `status` (`active`, `inactive`) | | `GET /v1/agenda` | `from`, `to` (obligatorios, como mucho 31 días de diferencia), `professionalId`, `centerId` | Los pacientes se ordenan por apellido. Los pacientes archivados no aparecen en las listas. Las páginas de fichas de pacientes cuentan para el [techo de fichas por hora](/es/concepts/rate-limits) según las fichas que devuelven. # Permisos de la API y del MCP Source: https://docs.brelio.com/es/concepts/permissions Qué permiso necesita cada operación, y cómo se combinan los permisos de una credencial con los de la persona y la clínica. Cada operación necesita un permiso. La credencial debe tenerlo y, si es una credencial personal, también la persona. ## Los permisos | Permiso | Área | Operaciones | | - | - | - | | `organization.centers.read` | `team` | Listar centros | | `professionals.read` | `team` | Listar profesionales | | `services.read` | `team` | Listar servicios | | `calendar.read` | `agenda` | Leer la agenda, buscar huecos libres | | `appointments.read` | `agenda` | Leer una cita | | `appointments.create` | `agenda` | Reservar una cita | | `appointments.reschedule` | `agenda` | Mover una cita | | `appointments.cancel` | `agenda` | Cancelar una cita | | `patients.administrative.read` | `patients` | Listar, buscar y leer pacientes | | `patients.administrative.create` | `patients` | Crear un paciente | | `patients.administrative.update` | `patients` | Cambiar los datos de un paciente | | `patients.administrative.archive` | `patients` | Archivar un paciente | | `integrations.keys.manage` | `integrations` | Claves de API, apps de partners, webhooks, la política de datos y sus decisiones | | `self` | `integrations` | Tus propias claves y conexiones (`/v1/me/*`), solo credenciales personales | La tabla completa, con la ruta REST, la herramienta MCP y el área de datos de cada operación, está en [Operaciones](/es/reference/operations). Una integración también puede leerla desde la API: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/platform-policy/permissions \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ## Cómo se decide el acceso En cada llamada, en este orden: 1. **Credencial.** Válida, sin revocar y vinculada a una clínica de Brelio. 2. **Plan.** El plan de la clínica incluye el acceso a la API (si no, `402 PLATFORM_PLAN_REQUIRED`). 3. **Límites.** Consulta los [límites de uso](/es/concepts/rate-limits). 4. **Permiso.** La credencial tiene el permiso de la operación; si es personal, la persona también lo tiene, en el centro de que se trate. 5. **Área de datos.** El [área de datos](/es/concepts/data-areas) de la operación está activada y, si son datos de pacientes, la credencial está aceptada. 6. **La entrada, la versión y las reglas propias de la operación**, exactamente como en la web. Un rechazo a partir del paso 2 queda en el registro de actividad de la clínica con su código, nunca con la entrada. ## Centros * **Las credenciales de la organización** ven todos los centros. * **Las credenciales personales** ven los centros que ve la persona. Filtrar por un `centerId` fuera de ellos responde `403 CENTER_CONTEXT_FORBIDDEN`. * Si la clínica tiene varios centros, las operaciones que crean algo en un centro (una cita) o buscan en el tiempo de un centro (huecos libres) necesitan `centerId`. Sin él responden `422 APPOINTMENT_CENTER_REQUIRED` o `422 AVAILABILITY_CENTER_REQUIRED`. ## Por MCP Un asistente solo **ve** las herramientas cuyo permiso tienes, así que la propia lista de herramientas te dice qué puedes hacer. Una herramienta que no puedes usar no aparece, en lugar de fallar al llamarla. ## Lo que ninguna credencial puede hacer * Crear claves o apps de partners, o revocar una credencial que no sea ella misma. * Activar un área de datos o aceptar datos de pacientes. * Dar a una clave un permiso que no tiene quien la crea (`403 CREDENTIAL_PERMISSION_NOT_HELD`). Eso siempre lo hace una persona con sesión en Brelio. # Límites de uso de la API Source: https://docs.brelio.com/es/concepts/rate-limits Ráfagas por credencial, ritmo sostenido por clínica y un techo horario de fichas de pacientes, con cabeceras RateLimit en cada respuesta. Los límites protegen a cada clínica de un cliente desbocado y a los pacientes de extracciones masivas. Para un uso normal son holgados. ## Los límites | Límite | Cuenta | Ventana | Por defecto | | - | - | - | - | | `burst` | Peticiones por **credencial** | 10 s | 100 | | `standard` | Peticiones por **clínica** a operaciones estándar, todas las credenciales juntas | 60 s | 600 | | `heavy` | Peticiones por **clínica** a operaciones pesadas (ninguna en esta versión) | 60 s | 30 | | `patient_records` | Fichas de pacientes **devueltas** a una credencial | 1 hora | 2.000 | | Pruebas de webhook | `POST /v1/webhooks/{id}/test` por webhook | 60 s | 5 | * `standard` lo comparten todas las claves, apps y asistentes de la clínica: un bucle en una integración puede frenar a las demás. Reparte los procesos por lotes. * `patient_records` cuenta lo que sale de Brelio: una página de 100 pacientes cuenta 100. Cuando se alcanza, se avisa a los propietarios de la clínica. * Una clínica puede tener límites más altos por acuerdo. ## Cabeceras Cada respuesta a una llamada autenticada lleva las cabeceras IETF `RateLimit-Policy` y `RateLimit`: ```text theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} RateLimit-Policy: "burst";q=100;w=10, "standard";q=600;w=60 RateLimit: "standard";r=599;t=52 ``` * `RateLimit-Policy` lista los límites que aplican: `q` es la cuota y `w` la ventana en segundos. * `RateLimit` informa del más cercano: `r` peticiones restantes, `t` segundos hasta que se reinicia. ## Cuando llegas a uno ```text theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} HTTP/1.1 429 Too Many Requests RateLimit-Policy: "burst";q=100;w=10, "standard";q=600;w=60 RateLimit: "standard";r=0;t=43 Retry-After: 43 ``` ```json 429 RATE_LIMITED theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "type": "https://api.brelio.app/problems/rate-limited", "title": "Too Many Requests", "status": 429, "code": "RATE_LIMITED", "detail": "The standard limit was reached.", "request_id": "a258784d-fd6b-4674-af1e-f653bf8ad727", "params": {}, "hint": "Wait 43 s before retrying; spread calls or ask for a higher limit.", "retryAfterSeconds": 43 } ``` Espera `Retry-After` segundos (también en `retryAfterSeconds`) antes de la siguiente llamada. Por MCP, el mismo objeto llega como resultado de error de la herramienta. ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} async function call(url, init) { for (;;) { const response = await fetch(url, init) if (response.status !== 429) return response const seconds = Number(response.headers.get('retry-after') ?? '1') await new Promise((resolve) => setTimeout(resolve, seconds * 1000)) } } ``` ## Si el limitador no responde Si el limitador no responde, las peticiones pasan, pero las operaciones con datos de pacientes se rechazan con `503 PLATFORM_LIMITS_UNAVAILABLE` y `Retry-After: 5`, para que nunca se salte el techo de fichas de pacientes. # Reserva una cita de principio a fin Source: https://docs.brelio.com/es/guides/book-appointment Busca un hueco libre, resérvalo, reprográmalo y cancélalo con la API de Brelio, con las mismas reglas que la agenda de la clínica. Esta guía reserva a un paciente con un profesional para un servicio, mueve la cita y la cancela. Usa `curl` y `jq`; cada paso es igual con cualquier cliente HTTP. ## Antes de empezar * Una [clave de API de la organización](/es/authentication#claves-de-api-de-la-organización) con `services.read`, `professionals.read`, `calendar.read`, `appointments.read`, `appointments.create`, `appointments.reschedule` y `appointments.cancel`, y `patients.administrative.read` para encontrar al paciente. * Las [áreas de datos](/es/concepts/data-areas) `agenda` y `team` activadas (lo están por defecto). Buscar al paciente por nombre necesita además el área `patients`; si está desactivada, usa un id de paciente que ya tengas. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} export BRELIO_API_KEY="…" export BRELIO=https://api.brelio.app/v1 ``` ## 1. Consigue los ids ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} PROFESSIONAL_ID=$(curl -s "$BRELIO/professionals?status=active" \ -H "Authorization: Bearer $BRELIO_API_KEY" | jq -r '.data[0].id') SERVICE_ID=$(curl -s "$BRELIO/services?search=fisio" \ -H "Authorization: Bearer $BRELIO_API_KEY" | jq -r '.data[0].id') PATIENT_ID=$(curl -s "$BRELIO/patients?search=ana" \ -H "Authorization: Bearer $BRELIO_API_KEY" | jq -r '.data[0].id') ``` Si la clínica tiene más de un centro, toma también un `centerId` de `GET /v1/centers` y envíalo en los pasos siguientes. ## 2. Busca huecos libres ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -G "$BRELIO/availability" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ --data-urlencode "professionalId=$PROFESSIONAL_ID" \ --data-urlencode "serviceId=$SERVICE_ID" \ --data-urlencode "patientId=$PATIENT_ID" \ --data-urlencode "from=2026-11-16T00:00:00Z" \ --data-urlencode "to=2026-11-17T00:00:00Z" ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ { "startAt": "2026-11-16T08:00:00.000Z", "endAt": "2026-11-16T08:45:00.000Z" }, { "startAt": "2026-11-16T08:30:00.000Z", "endAt": "2026-11-16T09:15:00.000Z" }, { "startAt": "2026-11-16T09:00:00.000Z", "endAt": "2026-11-16T09:45:00.000Z" } ], "durationMinutes": 45, "centerId": "82bae0e8-34b1-4f84-861a-472604262d3d" } ``` * Los huecos siguen el horario de la clínica y el intervalo de la agenda, y dejan fuera lo que el profesional ya tiene reservado o bloqueado: exactamente lo que aceptaría la agenda de Brelio. * Las horas están en UTC. La clínica del ejemplo está en Madrid (UTC+1), así que `08:00Z` son las 09:00 hora local. * La ventana es de 31 días como máximo y llegan como mucho 50 huecos. La duración es la del servicio salvo que envíes `durationMinutes`. ## 3. Resérvalo Envía uno de los `startAt` como `start`, con una `Idempotency-Key` nueva: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X POST "$BRELIO/appointments" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d "{ \"patientId\": \"$PATIENT_ID\", \"professionalId\": \"$PROFESSIONAL_ID\", \"serviceId\": \"$SERVICE_ID\", \"start\": \"2026-11-16T08:00:00.000Z\" }" ``` ```json 201 Created theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "8bd8c358-6e46-41ae-adc2-03772033149f:2026-11-16T08:00:00.000Z", "seriesId": "8bd8c358-6e46-41ae-adc2-03772033149f", "start": "2026-11-16T08:00:00.000Z", "end": "2026-11-16T08:45:00.000Z", "durationMinutes": 45, "status": "scheduled", "professionalId": "bd548adb-6b1a-477b-8830-480c6330e028", "serviceId": "c3c8e275-a6b1-4fba-9b17-9c0a01e3ffc8", "patientId": "c588eb77-12e8-4042-8cce-48fb0555408f", "centerId": "82bae0e8-34b1-4f84-861a-472604262d3d", "recurring": false, "version": "1", "createdAt": "2026-11-09T17:59:34.308Z", "updatedAt": "2026-11-09T17:59:34.308Z" } ``` Guarda el `id`. Es opaco: puede contener `:` y una fecha, y esa fecha no cambia cuando la cita se mueve. Úsalo tal cual. Si alguien ha ocupado el hueco desde que buscaste, la respuesta es `409 APPOINTMENT_OVERLAP_DETECTED`: vuelve a buscar y ofrece otra hora. ## 4. Léela ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} APPOINTMENT_ID="8bd8c358-6e46-41ae-adc2-03772033149f:2026-11-16T08:00:00.000Z" curl -s "$BRELIO/appointments/$APPOINTMENT_ID" \ -H "Authorization: Bearer $BRELIO_API_KEY" | jq '{status, start, version}' ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "status": "scheduled", "start": "2026-11-16T08:00:00.000Z", "version": "1" } ``` `GET /v1/agenda?from=…&to=…` lista las mismas citas de una ventana, con los mismos ids. ## 5. Reprográmala Envía la `version` que acabas de leer como `expectedVersion`, y un `start` nuevo de una nueva búsqueda de huecos: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X POST "$BRELIO/appointments/$APPOINTMENT_ID/reschedule" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": "1", "start": "2026-11-16T10:00:00Z"}' ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "8bd8c358-6e46-41ae-adc2-03772033149f:2026-11-16T08:00:00.000Z", "start": "2026-11-16T10:00:00.000Z", "end": "2026-11-16T10:45:00.000Z", "status": "scheduled", "version": "2", "…": "…" } ``` Si la cita cambió en Brelio desde que la leíste, recibes `409 VERSION_CONFLICT`: vuelve a leerla y decide otra vez. Consulta la [concurrencia](/es/concepts/concurrency). ## 6. Cancélala ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X POST "$BRELIO/appointments/$APPOINTMENT_ID/cancel" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": "2", "reason": "Patient asked to cancel by phone"}' ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "8bd8c358-…:2026-11-16T08:00:00.000Z", "status": "cancelled", "version": "3", "…": "…" } ``` * `reason` es solo administrativo. Nunca pongas información clínica. * Cancelar no se puede deshacer por la API. * Una cita que ya tiene sesión (paciente llegado o después) se cancela en Brelio: `409 APPOINTMENT_CANCEL_IN_BRELIO`. ## Límites de esta versión * Solo citas sueltas. Las series recurrentes responden `422 APPOINTMENT_RECURRING_UNSUPPORTED` al reprogramar o cancelar. * Reservar, reprogramar y cancelar es solo por REST; los asistentes MCP pueden leer la agenda y los huecos, pero todavía no escribir. * `patientId` en la agenda es `null` salvo que el área `patients` de la clínica esté activada y tu clave esté aceptada para datos de pacientes. ## Todo el flujo en Node.js ```javascript book.mjs theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} const BASE = 'https://api.brelio.app/v1' const AUTH = { Authorization: `Bearer ${process.env.BRELIO_API_KEY}` } async function api(path, { method = 'GET', body } = {}) { const response = await fetch(`${BASE}${path}`, { method, headers: body ? { ...AUTH, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() } : AUTH, body: body && JSON.stringify(body), }) const json = await response.json() if (!response.ok) throw Object.assign(new Error(`${json.code}: ${json.detail}`), { problem: json }) return json } const [professional] = (await api('/professionals?status=active')).data const [service] = (await api('/services?search=fisio')).data const [patient] = (await api('/patients?search=ana')).data const query = new URLSearchParams({ professionalId: professional.id, serviceId: service.id, patientId: patient.id, from: '2026-11-16T00:00:00Z', to: '2026-11-17T00:00:00Z', }) const [slot] = (await api(`/availability?${query}`)).data const booked = await api('/appointments', { method: 'POST', body: { patientId: patient.id, professionalId: professional.id, serviceId: service.id, start: slot.startAt }, }) console.log('booked', booked.start) const id = encodeURIComponent(booked.id) const moved = await api(`/appointments/${id}/reschedule`, { method: 'POST', body: { expectedVersion: booked.version, start: '2026-11-16T10:00:00Z' }, }) console.log('moved to', moved.start) const cancelled = await api(`/appointments/${id}/cancel`, { method: 'POST', body: { expectedVersion: moved.version, reason: 'Patient asked to cancel by phone' }, }) console.log(cancelled.status) ``` # Crea un agente de IA con el MCP de Brelio Source: https://docs.brelio.com/es/guides/mcp-agent Conecta un asistente de IA o tu propio agente al servidor MCP de Brelio, con prompts para probar y las reglas que sigue el servidor. El servidor MCP de Brelio convierte los datos de la clínica en herramientas que un modelo de IA puede llamar. Esta guía muestra prompts que funcionan bien, cómo se comporta el servidor y cómo llamarlo desde tu propio código. ## Conecta Sigue [Conecta tu asistente de IA](/es/quickstart/mcp). Para tu propio agente sirve cualquier cliente MCP que admita Streamable HTTP y OAuth: apúntalo a `https://api.brelio.app/mcp`. ## Prompts para probar Cópialos en tu asistente con el conector de Brelio activado. El asistente responde en el idioma en que escribes. | Prompt | Herramientas que usa | | - | - | | ¿Qué servicios ofrecemos y cuánto dura cada uno? | `brelio_list_services` | | ¿Qué profesionales trabajan en la clínica y cuáles están activos? | `brelio_list_professionals` | | ¿En qué centros trabajamos? ¿Cuál es el principal? | `brelio_list_centers` | | ¿Hay algún servicio de fisioterapia? ¿Cuánto cuesta? | `brelio_list_services` con `search` | | ¿Qué datos de pacientes pueden usar ahora mismo los asistentes? | `brelio_list_platform_policy` | | ¿Quién ha cambiado los permisos de datos de la clínica últimamente? | `brelio_list_platform_policy_decisions` | | ¿Qué webhooks tenemos y alguno está fallando? | `brelio_list_webhooks`, `brelio_get_webhooks` | | ¿Cuántas citas hay mañana y con quién? | `brelio_list_agenda` | | ¿Qué huecos libres tiene Laia el jueves por la mañana para fisioterapia? | `brelio_list_professionals`, `brelio_list_services`, `brelio_search_availability` | | Busca a la paciente Ana Pérez y dime su teléfono. | `brelio_list_patients` | Los tres últimos necesitan las herramientas de agenda y pacientes; consulta las [limitaciones conocidas](/es/changelog#limitaciones-conocidas). Los de pacientes necesitan además el [área de datos](/es/concepts/data-areas) de pacientes activada en la clínica. Un prompt más largo que prepara al agente para trabajar en recepción: ```text Prompt de sistema theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} Ayudas a la recepción de una clínica que usa Brelio. Usa solo las herramientas de Brelio para los datos de la clínica; nunca inventes nombres, horas ni precios. Las horas de Brelio están en UTC: pásalas a la zona horaria de la clínica (Europe/Madrid) antes de mostrarlas. Antes de proponer una cita, busca huecos libres con brelio_search_availability. Si una herramienta responde con un error, lee su `code` y su `hint` y explica qué puede hacer la persona. Nunca escribas información clínica en nada que envíes a Brelio. ``` ## Cómo se comporta el servidor * **Actúa como la persona que lo conectó**, en una clínica, con los permisos de esa persona leídos en cada llamada. Las herramientas que la persona no puede usar no aparecen. * **Solo herramientas de lectura, en esta versión.** Reservar, reprogramar, cancelar y cambiar pacientes es por [REST](/es/guides/book-appointment) de momento. Cuando lleguen las herramientas de escritura, las destructivas (cancelar, archivar, eliminar) pedirán confirmación a la persona antes de actuar. * **Cada herramienta tiene un JSON Schema de entrada y de salida**, devuelve `structuredContent` y lleva las anotaciones `readOnlyHint` y `destructiveHint`. * **Las listas están paginadas:** devuelve `next_cursor` como `cursor` mientras `has_more` sea true. * **Los errores son los mismos objetos problem que en REST**, devueltos como resultado de la herramienta con `isError: true`. Sigue el `hint` en lugar de reintentar a ciegas. * **Aceptación de datos de pacientes:** si la conexión no está aceptada para datos de pacientes, una herramienta de pacientes devuelve `PATIENT_DATA_CONSENT_REQUIRED`. Los clientes que admiten elicitation por URL abren la página de aceptación de Brelio; los demás reciben el enlace en `params.consent_url` y en el `hint`. * **Los secretos nunca pasan por MCP.** Las operaciones que revelan un secreto de un solo uso (el secreto de firma de un webhook) no se ofrecen, y se rechazan con `PLATFORM_REVEAL_FORBIDDEN`. La lista completa, con argumentos y ejemplos, está en [Herramientas MCP](/es/mcp/tools). ## Llámalo desde tu código El servidor es Streamable HTTP sin estado. Con un token de acceso para `resource=https://api.brelio.app/mcp`: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s https://api.brelio.app/mcp \ -H "Authorization: Bearer $MCP_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "MCP-Protocol-Version: 2025-06-18" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"brelio_list_services","arguments":{"limit":5}}}' ``` ```text Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} event: message data: {"result":{"structuredContent":{"data":[{"id":"c3c8e275-…","name":"Fisioterapia","defaultDuration":45,…}],"next_cursor":null,"has_more":false},"content":[{"type":"text","text":"{…}"}]},"jsonrpc":"2.0","id":1} ``` Sin token el servidor responde `401` con una cabecera `WWW-Authenticate` que apunta a sus [metadatos OAuth](/es/authentication#oauth-de-mcp); los SDK de MCP la siguen por ti. Con el SDK oficial de TypeScript: ```typescript agent.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' const transport = new StreamableHTTPClientTransport(new URL('https://api.brelio.app/mcp'), { requestInit: { headers: { Authorization: `Bearer ${process.env.MCP_ACCESS_TOKEN}` } }, }) const client = new Client({ name: 'my-clinic-agent', version: '1.0.0' }) await client.connect(transport) const { tools } = await client.listTools() console.log(tools.map((tool) => tool.name)) const services = await client.callTool({ name: 'brelio_list_services', arguments: { limit: 5 } }) console.log(services.structuredContent) ``` En producción, deja que el soporte OAuth del SDK (`authProvider`) obtenga y renueve el token en lugar de pasarlo a mano. ## Agentes que llaman a REST Un agente que escribe código contra la API, en lugar de usar herramientas MCP, puede partir del documento OpenAPI en `https://api.brelio.app/v1/openapi.json`. Cada operación lleva `x-brelio-permission`, `x-brelio-data-class`, `x-brelio-risk` y `x-brelio-examples`. Este prompt lo prepara: ```text Prompt theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} Quiero integrarme con la API de Brelio. - OpenAPI: https://api.brelio.app/v1/openapi.json - URL base: https://api.brelio.app/v1 - Autenticación: Authorization: Bearer , leída del entorno, nunca escrita en el código. - Cada POST/PATCH necesita una Idempotency-Key; las actualizaciones necesitan la expectedVersion de la última lectura. Antes de nada, haz un smoke test: sin clave la API debe responder 401; con la clave, GET /v1/services debe listar los servicios de la clínica. Usa solo operaciones del documento OpenAPI. ``` # Gestiona pacientes con la API Source: https://docs.brelio.com/es/guides/patients Busca, lee, crea, actualiza y archiva los datos administrativos de los pacientes de tu clínica con la API REST de Brelio. Las operaciones de pacientes cubren los datos administrativos: nombre, datos de contacto, fecha de nacimiento, códigos y estado. La historia clínica y las notas todavía no están en la API. ## Antes de empezar * El [área de datos](/es/concepts/data-areas) `patients` de la clínica está activada y se ha aceptado el acceso a datos de pacientes para tu credencial. Si no, cada llamada de esta guía responde `403 DATA_AREA_OFF` o `403 PATIENT_DATA_CONSENT_REQUIRED`. * Tu clave tiene `patients.administrative.read`, y `.create`, `.update` o `.archive` para las escrituras. * Cada ficha de paciente que recibes cuenta para el techo de [2.000 fichas por hora](/es/concepts/rate-limits) de tu credencial. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} export BRELIO_API_KEY="…" export BRELIO=https://api.brelio.app/v1 ``` ## Buscar `search` encuentra por nombre, email, teléfono, código de paciente, código interno o número de identificación, igual que el buscador de Brelio: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s "$BRELIO/patients?search=ana&limit=10" \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ { "id": "c588eb77-12e8-4042-8cce-48fb0555408f", "code": "PAC-0001", "firstName": "Ana", "lastName": "Pérez", "birthDate": null, "sex": null, "email": "ana.perez@example.com", "phone": null, "status": "active", "identificationNumber": null, "identificationType": "dni", "internalCode": null, "responsibleId": null, "admittedAt": null, "lastSessionAt": null, "nextSessionAt": null, "archivedAt": null, "version": "2026-11-09 17:59:08.46+00", "createdAt": "2026-11-09T17:59:08.460Z", "updatedAt": "2026-11-09T17:59:08.460Z" } ], "next_cursor": null, "has_more": false } ``` Filtra por `status` (`active`, `inactive`, `discharged`). Los resultados se ordenan por apellido y están [paginados](/es/concepts/pagination). ## Leer uno ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s "$BRELIO/patients/$PATIENT_ID" \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ## Crear Busca antes para no crear un duplicado. Solo `firstName` es obligatorio. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X POST "$BRELIO/patients" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "firstName": "Laura", "lastName": "Serra", "birthDate": "1990-05-14", "email": "laura.serra@example.com", "phone": "+34 600 123 456" }' ``` ```json 201 Created theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "d4ddb883-ee85-46d1-9719-5e0f7e585392", "code": "PAC-0003", "firstName": "Laura", "lastName": "Serra", "birthDate": "1990-05-14", "email": "laura.serra@example.com", "phone": "+34 600 123 456", "status": "active", "version": "2026-11-09 17:59:58.817+00", "…": "…" } ``` * `sex` acepta `male`, `female` u `other`. * `centerId` vincula al paciente con un centro; por defecto, el centro principal de la clínica. * Se aplican los mismos límites del plan que en la web, como el límite de pacientes activos. ## Actualizar Envía solo los campos que cambian, con la `version` que leíste como `expectedVersion`. `null` vacía un campo; `lastName` no se puede vaciar. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X PATCH "$BRELIO/patients/$PATIENT_ID" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "expectedVersion": "2026-11-09 17:59:58.817+00", "phone": "+34 600 765 432", "email": null }' ``` La respuesta es el paciente actualizado con una `version` nueva. Una `expectedVersion` antigua responde `409 VERSION_CONFLICT`; vuelve a leer el paciente. Consulta la [concurrencia](/es/concepts/concurrency). ## Archivar Archivar retira a un paciente: desaparece de las listas y las búsquedas, y `GET /v1/patients/{id}` responde `404 PATIENT_NOT_FOUND`, pero su historial se conserva y se puede recuperar en Brelio. ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -s -X POST "$BRELIO/patients/$PATIENT_ID/archive" \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": "2026-11-09 17:59:58.866+00"}' ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "d4ddb883-…", "status": "active", "archivedAt": "2026-11-09T17:59:58.967Z", "version": "2026-11-09 17:59:58.967+00", "…": "…" } ``` `status` y `archivedAt` son independientes: un paciente archivado conserva su último estado. No existe el borrado. ## Sincroniza los pacientes con otro sistema ```python sync_patients.py theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import os import requests BASE = "https://api.brelio.app/v1" HEADERS = {"Authorization": f"Bearer {os.environ['BRELIO_API_KEY']}"} def patients(**filters): cursor = None while True: params = {**filters, "limit": 100, **({"cursor": cursor} if cursor else {})} response = requests.get(f"{BASE}/patients", params=params, headers=HEADERS, timeout=10) if response.status_code == 429: raise RuntimeError(f"Rate limited; retry in {response.headers['Retry-After']} s") response.raise_for_status() page = response.json() yield from page["data"] if not page["has_more"]: return cursor = page["next_cursor"] for patient in patients(status="active"): print(patient["code"], patient["firstName"], patient["lastName"], patient["email"]) ``` Prográmalo en horas tranquilas y ten en cuenta el techo de 2.000 fichas por hora en clínicas grandes. # Recibe webhooks de Brelio Source: https://docs.brelio.com/es/guides/webhooks Suscribe una URL a los eventos de Brelio, verifica la firma de cada entrega en Node.js o Python y gestiona los reintentos. En lugar de consultar cada poco, tu servidor recibe un `POST` firmado cuando pasa algo en la clínica. Brelio firma las entregas con el esquema [Standard Webhooks](https://www.standardwebhooks.com), así que cualquier librería de Standard Webhooks las verifica. > [!NOTE] > > En esta versión el único evento al que puedes suscribirte es `webhookEndpoint.tested`, la entrega de prueba. Los eventos de citas y pacientes son los siguientes; `GET /v1/webhook-event-types` siempre lista los disponibles. ## 1. Lista los tipos de evento ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/webhook-event-types \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ { "type": "webhookEndpoint.tested", "version": 1, "description": "A test delivery was requested for a webhook endpoint.", "module": "webhooks", "privacy": "operational" } ] } ``` `privacy` es `operational` o `personal` (sobre pacientes). Los eventos clínicos nunca se entregan por webhook. Los eventos `personal` solo se entregan mientras el [área de datos](/es/concepts/data-areas) `patients` de la clínica está activada; mientras está desactivada, los campos de pacientes de otros eventos llegan como `null`. ## 2. Crea el webhook Tu clave necesita `integrations.keys.manage`. La URL debe ser `https` en un nombre de host público (sin direcciones IP ni `localhost`). ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/webhooks \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "url": "https://hooks.example.com/brelio", "description": "CRM de la clínica", "eventTypes": ["webhookEndpoint.tested"] }' ``` ```json 201 Created theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "e91750cd-0f3b-4acb-800a-1c7323e66bd7", "url": "https://hooks.example.com/brelio", "description": "CRM de la clínica", "eventTypes": ["webhookEndpoint.tested"], "status": "enabled", "secretHint": "whsec_…6OQ=", "secretCreatedAt": "2026-11-09T18:01:30.790Z", "previousSecretExpiresAt": null, "failingSince": null, "disablesAt": null, "disabledAt": null, "createdBy": "api_key_85C593155A6442B58528D38FC59B0CE9", "version": 1, "createdAt": "2026-11-09T18:01:30.790Z", "updatedAt": "2026-11-09T18:01:30.790Z", "secret": "whsec_iSiK6+Zxx2+8QtctJLM+19HZZ+hc9hlmlXi67gh66OQ=" } ``` Guarda `secret` ahora, en tu gestor de secretos: solo se muestra aquí y al rotarlo. Los webhooks se crean solo por REST, nunca por MCP, así que el secreto nunca llega a un modelo de IA. ## 3. Verifica cada entrega Cada entrega es un `POST` con un cuerpo JSON y tres cabeceras: ```http theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} POST /brelio HTTP/1.1 Content-Type: application/json User-Agent: Brelio-Webhooks/1 webhook-id: 5c2f8b7e-1d3a-4e6f-8a9b-0c1d2e3f4a5b webhook-timestamp: 1794247290 webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4= {"id":"0d8b3c1e-…","type":"webhookEndpoint.tested","version":1,"timestamp":"2026-11-09T18:01:30.851Z","data":{"endpointId":"e91750cd-0f3b-4acb-800a-1c7323e66bd7"}} ``` La firma es `base64(HMAC-SHA256(clave, "{webhook-id}.{webhook-timestamp}.{cuerpo sin procesar}"))`, donde `clave` es la parte de tu secreto después de `whsec_`, decodificada de base64. Para verificarla: 1. Calcula la firma sobre los bytes **sin procesar** del cuerpo, antes de interpretar el JSON. 2. Compárala en tiempo constante con cada entrada `v1,` de `webhook-signature` (separadas por espacios: durante una rotación del secreto hay dos). 3. Rechaza marcas de tiempo a más de 5 minutos de tu reloj, para evitar reenvíos. 4. Responde `2xx` rápido; haz el trabajo después. ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import { createHmac, timingSafeEqual } from 'node:crypto' import { createServer } from 'node:http' const SECRET = process.env.BRELIO_WEBHOOK_SECRET // whsec_… const TOLERANCE_SECONDS = 5 * 60 /** Standard Webhooks: HMAC-SHA256 of `${id}.${timestamp}.${body}` with the base64 key after `whsec_`. */ export function verifyBrelioWebhook(secret, headers, rawBody) { const id = headers['webhook-id'] const timestamp = headers['webhook-timestamp'] const signatures = headers['webhook-signature'] if (!id || !timestamp || !signatures) return false if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64') const expected = createHmac('sha256', key).update(`${id}.${timestamp}.${rawBody}`).digest() // During a secret rotation Brelio signs with both secrets: `v1, v1,`. return signatures.split(' ').some((candidate) => { const [version, signature] = candidate.split(',') if (version !== 'v1' || !signature) return false const received = Buffer.from(signature, 'base64') return received.length === expected.length && timingSafeEqual(received, expected) }) } const seen = new Set() // use your database in production createServer((req, res) => { let rawBody = '' req.on('data', (chunk) => (rawBody += chunk)) req.on('end', () => { if (!verifyBrelioWebhook(SECRET, req.headers, rawBody)) { res.writeHead(400).end('invalid signature') return } const id = req.headers['webhook-id'] if (!seen.has(id)) { seen.add(id) const event = JSON.parse(rawBody) console.log('received', event.type, event.id) } res.writeHead(204).end() }) }).listen(3000) ``` ```python Python (Flask) theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import base64, hashlib, hmac, json, os, time from flask import Flask, request SECRET = os.environ["BRELIO_WEBHOOK_SECRET"] # whsec_… TOLERANCE_SECONDS = 5 * 60 def verify_brelio_webhook(secret: str, headers, raw_body: bytes) -> bool: """Standard Webhooks: HMAC-SHA256 of f"{id}.{timestamp}.{body}" with the base64 key after whsec_.""" msg_id = headers.get("webhook-id") timestamp = headers.get("webhook-timestamp") signatures = headers.get("webhook-signature") if not msg_id or not timestamp or not signatures or not timestamp.isdigit(): return False if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS: return False key = base64.b64decode(secret.removeprefix("whsec_")) signed = f"{msg_id}.{timestamp}.".encode() + raw_body expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode() # During a secret rotation Brelio signs with both secrets: "v1, v1,". return any( candidate.startswith("v1,") and hmac.compare_digest(candidate[3:], expected) for candidate in signatures.split(" ") ) app = Flask(__name__) seen: set[str] = set() # use your database in production @app.post("/brelio") def brelio_webhook(): raw_body = request.get_data() # the exact bytes, before any JSON parsing if not verify_brelio_webhook(SECRET, request.headers, raw_body): return "invalid signature", 400 msg_id = request.headers["webhook-id"] if msg_id not in seen: seen.add(msg_id) event = json.loads(raw_body) print("received", event["type"], event["id"]) return "", 204 ``` O usa las librerías oficiales de Standard Webhooks, que hacen las mismas comprobaciones: ```javascript Node.js (standardwebhooks) theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import { createServer } from 'node:http' import { Webhook } from 'standardwebhooks' const webhook = new Webhook(process.env.BRELIO_WEBHOOK_SECRET.replace(/^whsec_/, '')) createServer((req, res) => { let rawBody = '' req.on('data', (chunk) => (rawBody += chunk)) req.on('end', () => { try { const event = webhook.verify(rawBody, req.headers) console.log('received', event.type, event.id) res.writeHead(204).end() } catch { res.writeHead(400).end('invalid signature') } }) }).listen(3000) ``` ```python Python (standardwebhooks) theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import os from flask import Flask, request from standardwebhooks.webhooks import Webhook wh = Webhook(os.environ["BRELIO_WEBHOOK_SECRET"]) app = Flask(__name__) @app.post("/brelio") def brelio_webhook(): try: event = wh.verify(request.get_data(), dict(request.headers)) except Exception: return "invalid signature", 400 print("pylib ok", event["type"]) return "", 204 ``` Instálalas con `npm install standardwebhooks` o `pip install standardwebhooks`. ## 4. Envía una prueba ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/webhooks/$WEBHOOK_ID/test \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "e91750cd-0f3b-4acb-800a-1c7323e66bd7", "eventType": "webhookEndpoint.tested" } ``` La prueba se entrega a este webhook, esté suscrito a lo que esté. Como mucho 5 pruebas por minuto por webhook (`429 WEBHOOK_TEST_RATE_LIMITED`). ## El cuerpo de la entrega | Campo | Significado | | - | - | | `id` | El id del evento. El mismo evento entregado a dos webhooks tiene el mismo `id`. | | `type` | El tipo de evento, por ejemplo `webhookEndpoint.tested`. | | `version` | La versión de la forma de `data`. Un cambio incompatible sale como versión nueva. | | `timestamp` | Cuándo ocurrió el evento, en ISO 8601. | | `data` | El contenido del evento. | ## Reintentos y duplicados * Una entrega tiene éxito cuando tu servidor responde `2xx` en menos de 15 segundos. No se siguen redirecciones. * Una entrega fallida se reintenta a los 5 s, 5 min, 30 min, 2 h, 5 h, 10 h y después cada 12 h, durante 3 días. * Si no llega nada en 3 días, el webhook se **desactiva** y se avisa a los propietarios de la clínica. `failingSince` y `disablesAt` del webhook te avisan antes. * `webhook-id` es el id de la entrega y es el mismo en cada reintento y en un reenvío manual. Guarda los ids que ya procesaste y descarta los repetidos. * Las entregas pueden llegar desordenadas. Usa `timestamp`, o lee el registro desde la API, cuando el orden importe. ## Diagnostica un webhook ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/webhooks/$WEBHOOK_ID \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` `recentDeliveries` muestra de cada entrega su `status` (`pending`, `succeeded`, `failed`, `skipped`), `attempts`, `lastStatusCode`, `lastError` y `nextAttemptAt`. Cuando hayas arreglado tu servidor, reenvía una entrega ya en lugar de esperar: ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/webhooks/$WEBHOOK_ID/redeliver \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"deliveryId": "5c2f8b7e-1d3a-4e6f-8a9b-0c1d2e3f4a5b"}' ``` ## Rota el secreto ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/webhooks/$WEBHOOK_ID/rotate-secret \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": 1}' ``` La respuesta trae el nuevo `secret`. Durante 24 horas (`previousSecretExpiresAt`) las entregas se firman con los dos secretos, para que puedas desplegar el nuevo sin perder nada. Los receptores de arriba ya aceptan cualquiera de las dos firmas. ## Elimínalo ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -X POST https://api.brelio.app/v1/webhooks/$WEBHOOK_ID/delete \ -H "Authorization: Bearer $BRELIO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"expectedVersion": 2}' ``` ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "e91750cd-0f3b-4acb-800a-1c7323e66bd7", "deleted": true } ``` Eliminarlo borra el webhook y su registro de entregas; no se envía nada más. # API y servidor MCP de Brelio Source: https://docs.brelio.com/es/index Conecta tu asistente de IA o tu propio software a Brelio, el software de gestión para clínicas: API REST, servidor MCP, permisos y datos de pacientes. > [card] [Agentes de IA](/es/agents) > > Conecta Claude, ChatGPT, Cursor o VS Code con el servidor MCP. Sin claves. > [card] [Primera llamada a la API](/es/quickstart/api) > > Crea una clave de API y lista los servicios de tu clínica con `curl`. ## API REST o servidor MCP | | API REST | Servidor MCP | | - | - | - | | Dirección | `https://api.brelio.app/v1` | `https://api.brelio.app/mcp` | | Quién llama | Tu código: scripts, integraciones, software de partners | Un asistente de IA: Claude, ChatGPT, Cursor, VS Code, Codex, Gemini | | Credencial | Una clave de API o el token de una app de partner | Tu cuenta de Brelio, con OAuth; sin clave | | Actúa como | La clínica (clave de la organización, app de partner) o una persona (clave personal) | La persona que lo conectó | | Qué puede hacer hoy | Leer y escribir: reservar, reprogramar y cancelar citas, gestionar pacientes y webhooks | Solo leer; las escrituras van por REST. Consulta las [limitaciones conocidas](/es/changelog#limitaciones-conocidas) | ## Guías > [card] [Crea un agente con MCP](/es/guides/mcp-agent) > > Un asistente para recepción que consulta los huecos reales de la agenda. > [card] [Reserva una cita](/es/guides/book-appointment) > > Busca un hueco libre, resérvalo, reprográmalo y cancélalo. > [card] [Gestiona pacientes](/es/guides/patients) > > Mantén un CRM o una centralita al día con los pacientes. > [card] [Recibe webhooks](/es/guides/webhooks) > > Eventos firmados para n8n, Make o tus propios scripts. ## Lo que siempre se cumple > [card] Las mismas reglas que la web > > Los mismos permisos, la misma validación y los mismos límites del plan. > [card] [Datos de pacientes cerrados](/es/concepts/data-areas) > > La clínica decide qué áreas de datos puede usar cada integración. > [card] [Los cambios nunca se pisan](/es/concepts/concurrency) > > Cada registro lleva una `version` que devuelves como `expectedVersion`. > [card] [Reintentar es seguro](/es/concepts/idempotency) > > Cada escritura lleva una `Idempotency-Key`. > [card] [Errores con código estable](/es/concepts/errors) > > Un único formato JSON con un `code` y, cuando lo hay, un `hint`. > [card] Todo queda registrado > > La clínica ve cada conexión y lo que hizo, también los intentos rechazados. > [!NOTE] > > La API y el servidor MCP están en acceso anticipado. Una organización sin acceso recibe `402 PLATFORM_PLAN_REQUIRED`. La API, sus textos de error y el documento OpenAPI están en inglés; esta documentación está en español y en inglés. ## Recursos para máquinas | Recurso | Dirección | | - | - | | Documento OpenAPI 3.1 | `https://api.brelio.app/v1/openapi.json` | | Servidor MCP | `https://api.brelio.app/mcp` | | Metadatos OAuth del recurso protegido | `https://api.brelio.app/.well-known/oauth-protected-resource/mcp` | | Esta documentación en texto plano, para LLM | `https://docs.brelio.com/llms.txt` | # Herramientas del servidor MCP Source: https://docs.brelio.com/es/mcp/tools Las herramientas que el servidor MCP de Brelio ofrece a tu asistente de IA, con el permiso, el área de datos y la ruta REST de cada una. Tu asistente solo ve las herramientas que tus permisos en Brelio te dejan usar. Esta página se genera desde el catálogo de operaciones: no la edites a mano. ## `brelio_list_agenda` **Agenda** · Solo lectura Lee la agenda de la clínica: las citas de un intervalo de tiempo, con inicio y fin, estado, profesional, servicio, paciente y centro. **Necesita:** permiso `calendar.read` · área de datos `agenda` · REST `GET /v1/agenda` **Cuándo usarla:** Para ver qué citas hay en un día o una semana, o las de un profesional o un centro. Como máximo 31 días por llamada; no devuelve notas ni nombres. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | | `from` | string | Inicio del intervalo, ISO 8601 (incluido). | | `to` | string | Fin del intervalo, ISO 8601. Como máximo 31 días después de `from`. | | `professionalId` | string | Solo las citas de este profesional. | | `centerId` | string | Solo las citas de este centro. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "from": "2026-11-09T00:00:00Z", "to": "2026-11-16T00:00:00Z", "limit": 50 } ``` ## `brelio_list_api_keys` **Claves de API de la organización** · Solo lectura Lista las claves de API de la organización, sin su secreto. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/api-keys` **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 100 } ``` ## `brelio_revoke_api_keys` **Revocar clave de API** · Pide confirmación antes de actuar > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Revoca una clave de API de la organización. Una clave solo puede revocarse a sí misma; revocar otras requiere la sesión de una persona. No se puede deshacer. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `POST /v1/api-keys/{id}/revoke` **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "api_key_01EXAMPLEORG" } ``` ## `brelio_cancel_appointments` **Cancelar cita** · Pide confirmación antes de actuar > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Cancela una cita puntual. No se puede deshacer desde la API. **Necesita:** permiso `appointments.cancel` · área de datos `agenda` · REST `POST /v1/appointments/{id}/cancel` **Cuándo usarla:** Solo cuando te pidan cancelar esa cita. Léela antes con appointments.get y envía su versión como expectedVersion; si cambió entretanto, la respuesta es 409. Las citas recurrentes no se pueden cancelar aquí. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id que devuelve agenda.list, o el id de la serie de citas. | | `expectedVersion` | string | La `version` de la cita tal como la devolvió `appointments.get`. No la construyas tú. | | `reason` | string \| null | Por qué se canceló, solo en términos administrativos. No incluyas nunca información clínica. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "6a1e4c2b-8d3f-4e5a-9b7c-0d1e2f3a4b5c", "expectedVersion": "1" } ``` ## `brelio_create_appointments` **Crear cita** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Reserva una cita de un paciente con un profesional para un servicio, con las mismas reglas que la agenda de Brelio. **Necesita:** permiso `appointments.create` · área de datos `agenda` · REST `POST /v1/appointments` **Cuándo usarla:** Después de elegir una hora con availability.search. Si la hora ya no está libre, la respuesta es 409: vuelve a buscar. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `patientId` | string | | | `professionalId` | string | | | `serviceId` | string | | | `start` | string | Cuándo empieza, ISO 8601. Tómalo de availability.search. | | `durationMinutes` | integer | Por defecto, la duración del servicio. | | `centerId` | string | Obligatorio cuando la organización tiene más de un centro. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "patientId": "9e2d4c6b-1a3f-4b5d-8c7e-6f5a4b3c2d1e", "professionalId": "2c4e6a8b-0d1f-4a3c-9e5b-7d9f1b3d5f7a", "serviceId": "4d6f8b0c-2e4a-4c6e-8a0c-2e4a6c8e0a2c", "start": "2026-11-11T09:00:00Z" } ``` ## `brelio_get_appointments` **Cita** · Solo lectura Devuelve una cita: cuándo es, su estado, profesional, servicio, paciente, centro y versión. **Necesita:** permiso `appointments.read` · área de datos `agenda` · REST `GET /v1/appointments/{id}` **Cuándo usarla:** Antes de mover o cancelar una cita, para leer su versión actual; o para comprobar su estado. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id que devuelve agenda.list, o el id de la serie de citas. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "6a1e4c2b-8d3f-4e5a-9b7c-0d1e2f3a4b5c" } ``` ## `brelio_reschedule_appointments` **Mover cita** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Mueve una cita puntual a otra hora, con las mismas reglas que la agenda de Brelio. No admite citas recurrentes. **Necesita:** permiso `appointments.reschedule` · área de datos `agenda` · REST `POST /v1/appointments/{id}/reschedule` **Cuándo usarla:** Lee antes la cita con appointments.get y envía su versión como expectedVersion; si cambió, recibes 409 VERSION\_CONFLICT. Elige la nueva hora con availability.search; si ya no está libre, 409. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id que devuelve agenda.list, o el id de la serie de citas. | | `expectedVersion` | string | La `version` de la cita tal como la devolvió appointments.get. No la construyas tú. | | `start` | string | El nuevo inicio, ISO 8601. Tómalo de availability.search. | | `durationMinutes` | integer | La nueva duración. Por defecto, la duración actual de la cita. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "6a1e4c2b-8d3f-4e5a-9b7c-0d1e2f3a4b5c", "expectedVersion": "1", "start": "2026-11-12T10:00:00Z" } ``` ## `brelio_search_availability` **Huecos libres** · Solo lectura Busca huecos libres para reservar una cita de un paciente con un profesional para un servicio: hasta 50 horas de inicio, con las mismas reglas que la agenda de Brelio. **Necesita:** permiso `calendar.read` · área de datos `agenda` · REST `GET /v1/availability` **Cuándo usarla:** Antes de crear una cita, para ofrecer horas que se puedan reservar sin conflicto. Intervalos de hasta 31 días. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `professionalId` | string | El profesional de la cita. | | `patientId` | string | El paciente de la cita, si se conoce; no cambia los huecos, que son el tiempo del profesional. | | `serviceId` | string | El servicio de la cita; su duración es la duración por defecto. | | `centerId` | string | Obligatorio cuando la organización tiene más de un centro. | | `from` | string | Inicio del intervalo, ISO 8601. | | `to` | string | Fin del intervalo, ISO 8601. Como máximo 31 días después de `from`. | | `durationMinutes` | integer | Duración de la cita. Por defecto, la duración del servicio. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "professionalId": "7d1f0c2e-5a3b-4c8d-9e6f-1a2b3c4d5e6f", "patientId": "5c2e8d1a-9b3f-4a6e-8d7c-2f1e0a9b8c7d", "serviceId": "3b9e7f1a-2c4d-4e6f-8a0b-1c2d3e4f5a6b", "from": "2026-11-09T00:00:00Z", "to": "2026-11-16T00:00:00Z" } ``` ## `brelio_list_centers` **Centros** · Solo lectura Lista los centros de la organización. El centro por defecto lleva isDefault. **Necesita:** permiso `organization.centers.read` · área de datos `team` · REST `GET /v1/centers` **Cuándo usarla:** Para saber en qué centros trabaja la clínica o para obtener el id de un centro antes de filtrar por él. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 100 } ``` ## `brelio_list_me_api_keys` **Mis claves de API** · Solo lectura Lista las claves de API de la persona en esta organización. **Necesita:** permiso `self` · área de datos `integrations` · REST `GET /v1/me/api-keys` **Cuándo usarla:** Para que una persona gestione sus propias claves y conexiones. Una clave de organización no puede usarla, y crear o revocar requiere la sesión de la persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 100 } ``` ## `brelio_list_me_connections` **Mis conexiones** · Solo lectura Lista las aplicaciones y asistentes que la persona autorizó a actuar por ella. **Necesita:** permiso `self` · área de datos `integrations` · REST `GET /v1/me/connections` **Cuándo usarla:** Para que una persona gestione sus propias claves y conexiones. Una clave de organización no puede usarla, y crear o revocar requiere la sesión de la persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 100 } ``` ## `brelio_list_partner_apps` **Apps de partners** · Solo lectura Lista las apps de partners (máquina a máquina) de la organización, sin su secreto. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/partner-apps` **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 100 } ``` ## `brelio_revoke_partner_apps` **Revocar app de partner** · Pide confirmación antes de actuar > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Elimina una app de partner; sus tokens dejan de funcionar. Una aplicación solo puede eliminarse a sí misma; eliminar otras requiere la sesión de una persona. No se puede deshacer. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `POST /v1/partner-apps/{id}/revoke` **Cuándo usarla:** Para gestionar el acceso de programas de la organización (integraciones, scripts) sin la sesión de una persona. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "conn_app_01EXAMPLE" } ``` ## `brelio_archive_patients` **Archivar paciente** · Pide confirmación antes de actuar > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Archiva un paciente: deja de aparecer en listas y búsquedas, pero se conserva su historial. Se puede deshacer en la web. **Necesita:** permiso `patients.administrative.archive` · área de datos `patients` · REST `POST /v1/patients/{id}/archive` **Cuándo usarla:** Cuando la clínica pida retirar un paciente. Lee antes el paciente con patients.get y envía su versión como expectedVersion; si cambió, recibes un 409. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id del paciente. | | `expectedVersion` | string | La `version` del paciente tal como la leíste. No la construyas tú. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0f6c2a8e-4b1d-4c3a-9e57-2d8b1f0a6c41", "expectedVersion": "2026-01-15 10:00:00+00" } ``` ## `brelio_create_patients` **Crear paciente** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Da de alta un paciente nuevo con sus datos administrativos y lo vincula a un centro. **Necesita:** permiso `patients.administrative.create` · área de datos `patients` · REST `POST /v1/patients` **Cuándo usarla:** Cuando el paciente aún no existe: busca antes con patients.list para no crear un duplicado. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `firstName` | string | | | `lastName` | string | | | `birthDate` | string | | | `sex` | `male`, `female`, `other` | | | `email` | string | | | `phone` | string | | | `centerId` | string | Centro al que pertenece el paciente. Por defecto, el centro por defecto de la organización. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "firstName": "Laura", "lastName": "Serra", "birthDate": "1990-05-14", "email": "laura.serra@example.com", "phone": "+34 600 123 456" } ``` ## `brelio_get_patients` **Paciente** · Solo lectura Devuelve los datos administrativos de un paciente: nombre, contacto, fecha de nacimiento y estado. **Necesita:** permiso `patients.administrative.read` · área de datos `patients` · REST `GET /v1/patients/{id}` **Cuándo usarla:** Cuando ya tienes el id del paciente y necesitas sus datos de contacto o su estado. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id del paciente. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0f6c2a8e-4b1d-4c3a-9e57-2d8b1f0a6c41" } ``` ## `brelio_list_patients` **Pacientes** · Solo lectura Lista los pacientes de la clínica con sus datos administrativos, ordenados por apellido. **Necesita:** permiso `patients.administrative.read` · área de datos `patients` · REST `GET /v1/patients` **Cuándo usarla:** Para encontrar un paciente por nombre, email o teléfono y obtener su id antes de leer su ficha o darle cita. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | | `search` | string | Busca en nombre, email, teléfono, código de paciente, código interno o número de identificación, igual que la búsqueda de la web. | | `status` | `active`, `inactive`, `discharged` | Solo los pacientes en este estado. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 25 } ``` ## `brelio_update_patients` **Actualizar paciente** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Cambia los datos administrativos de un paciente (nombre, contacto, fecha de nacimiento). Solo cambian los campos que envías; null vacía un campo. **Necesita:** permiso `patients.administrative.update` · área de datos `patients` · REST `PATCH /v1/patients/{id}` **Cuándo usarla:** Cuando haya que corregir o completar los datos de un paciente. Lee antes el paciente con patients.get y envía su versión como expectedVersion; si cambió entretanto, la respuesta es 409 y tienes que volver a leerlo. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | El id del paciente. | | `expectedVersion` | string | La `version` del paciente tal como la leíste. No la construyas tú. | | `firstName` | string | | | `lastName` | string | No se puede vaciar, igual que en la web. | | `birthDate` | string \| null | Envía null para vaciarlo. | | `sex` | `male`, `female`, `other` \| null | Envía null para vaciarlo. | | `email` | string \| null | Envía null para vaciarlo. | | `phone` | string \| null | Envía null para vaciarlo. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0f6c2a8e-4b1d-4c3a-9e57-2d8b1f0a6c41", "expectedVersion": "2026-01-15 10:00:00+00", "phone": "+34 600 765 432" } ``` ## `brelio_list_platform_policy_decisions` **Historial de decisiones** · Solo lectura Historial de decisiones sobre los datos de la clínica, de la más reciente a la más antigua: quién, cuándo y con qué condiciones. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/platform-policy/decisions` **Cuándo usarla:** Para auditar quién activó o desactivó un área, o quién aceptó datos de pacientes para una conexión. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | | `subjectType` | `organization`, `credential` | | | `subjectId` | string | | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 25 } ``` ## `brelio_list_platform_policy` **Datos que pueden usar las integraciones** · Solo lectura Qué áreas de datos (agenda, equipo, pacientes, facturación) permite la clínica que usen los asistentes y las integraciones. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/platform-policy` **Cuándo usarla:** Cuando una operación responde DATA\_AREA\_OFF, para ver qué está desactivado y decírselo a la persona. ### Argumentos Sin argumentos. ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} {} ``` ## `brelio_list_professionals` **Profesionales** · Solo lectura Lista los profesionales del equipo de la clínica con su estado. **Necesita:** permiso `professionals.read` · área de datos `team` · REST `GET /v1/professionals` **Cuándo usarla:** Para saber quién atiende en la clínica o para obtener el id de un profesional antes de consultar su agenda. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | | `status` | `pending`, `active`, `inactive` | Solo los profesionales en este estado. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 25 } ``` ## `brelio_list_services` **Servicios** · Solo lectura Lista los servicios que ofrece la clínica: nombre, duración, estado y precio. **Necesita:** permiso `services.read` · área de datos `team` · REST `GET /v1/services` **Cuándo usarla:** Para saber qué se puede reservar, cuánto dura o cuánto cuesta, o para obtener el id de un servicio. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | | `status` | `active`, `inactive` | Solo los registros cuyo campo «estado» tiene este valor. | | `eligiblePlanId` | string | Solo los registros cuyo campo «bono» tiene este valor. | | `activePackageAssignments` | `has`, `none` | Solo los registros cuyo campo «asignaciones activas» tiene este valor. | | `search` | string | Texto que se busca en los campos de búsqueda de «servicio». | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 25 } ``` ## `brelio_delete_webhooks` **Eliminar webhook** · Pide confirmación antes de actuar > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Elimina un webhook y su registro de entregas; deja de recibir avisos al momento. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `POST /v1/webhooks/{id}/delete` **Cuándo usarla:** Cuando un sistema ya no deba recibir los avisos de la clínica. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | Id del endpoint del webhook. | | `expectedVersion` | integer | La `version` que leíste. La llamada falla con 409 si el endpoint cambió desde entonces, y nunca sobrescribe. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0b9a4a1e-6a4c-4d43-9a39-4c1f3a0d2b11", "expectedVersion": 1 } ``` ## `brelio_list_webhook_event_types` **Tipos de evento de webhook** · Solo lectura Lista los eventos a los que se puede suscribir un webhook. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/webhook-event-types` **Cuándo usarla:** Antes de crear un webhook, para elegir sus eventos. ### Argumentos Sin argumentos. ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} {} ``` ## `brelio_get_webhooks` **Webhook** · Solo lectura Muestra un webhook con sus últimas entregas: respuesta, intentos y siguiente reintento. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/webhooks/{id}` **Cuándo usarla:** Para diagnosticar por qué un sistema no recibe avisos. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | Id del endpoint del webhook. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0b9a4a1e-6a4c-4d43-9a39-4c1f3a0d2b11" } ``` ## `brelio_list_webhooks` **Webhooks** · Solo lectura Lista los webhooks de la organización, su estado y desde cuándo fallan. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `GET /v1/webhooks` **Cuándo usarla:** Para revisar qué sistemas reciben avisos o para encontrar el id de un webhook. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `cursor` | string | `next_cursor` de la página anterior. Omítelo para la primera página. | | `limit` | integer | Tamaño de página, de 1 a 100. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "limit": 25 } ``` ## `brelio_redeliver_webhooks` **Reenviar** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Vuelve a enviar una entrega ahora, con el mismo webhook-id para que el receptor no la procese dos veces. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `POST /v1/webhooks/{id}/redeliver` **Cuándo usarla:** Después de arreglar el servidor que fallaba, en lugar de esperar al siguiente reintento. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | Id del endpoint del webhook. | | `deliveryId` | string | Una entrega de `recentDeliveries`. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0b9a4a1e-6a4c-4d43-9a39-4c1f3a0d2b11", "deliveryId": "5c2f8b7e-1d3a-4e6f-8a9b-0c1d2e3f4a5b" } ``` ## `brelio_test_webhooks` **Probar webhook** · Escribe datos > [!WARNING] > > Todavía no se ofrece por MCP: en esta versión el servidor MCP solo registra herramientas de lectura. Usa el endpoint REST. Envía un aviso de prueba firmado al webhook, sea cual sea su suscripción. **Necesita:** permiso `integrations.keys.manage` · área de datos `integrations` · REST `POST /v1/webhooks/{id}/test` **Cuándo usarla:** Para comprobar que el servidor recibe y verifica los avisos. ### Argumentos | Nombre | Tipo | Descripción | | - | - | - | | `id` | string | Id del endpoint del webhook. | ### Ejemplo ```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "id": "0b9a4a1e-6a4c-4d43-9a39-4c1f3a0d2b11" } ``` # Skill para agentes Source: https://docs.brelio.com/es/quickstart/agent-skill La skill oficial de Brelio enseña a Claude Code, Cursor, Codex y otros agentes a usar el servidor MCP y la API REST con seguridad. La skill de Brelio es un archivo `SKILL.md` que un agente de IA carga cuando una tarea tiene que ver con Brelio. Conectar el [servidor MCP](/es/quickstart/mcp) o una [clave de API](/es/quickstart/api) le da acceso; la skill le da las reglas de Brelio para que use bien ese acceso. **Archivo:** [docs.brelio.com/skill.md](https://docs.brelio.com/skill.md) ## Qué enseña * Cuándo usar una herramienta MCP y cuándo llamar a REST (el servidor MCP solo ofrece herramientas de lectura). * A confirmar contigo cada cambio antes de hacerlo y a no inventar nunca datos de pacientes, horas ni ids. * A pasar `centerId` siempre: una consulta de agenda sin él solo lee tu centro por defecto. * [`Idempotency-Key`](/es/concepts/idempotency) en cada escritura y [`expectedVersion`](/es/concepts/concurrency) en cada cambio. * Las [áreas de datos y el acceso a datos de pacientes](/es/concepts/data-areas): qué significan `DATA_AREA_OFF` y `PATIENT_DATA_CONSENT_REQUIRED` y qué decirte. * Los [códigos de error](/es/concepts/errors) con su solución y los [límites de uso](/es/concepts/rate-limits). * Cada herramienta y operación, con sus argumentos, su permiso y su área de datos. La parte de referencia se genera desde el mismo catálogo que la API, así que siempre coincide con las páginas de [herramientas MCP](/es/mcp/tools) y [operaciones](/es/reference/operations). La skill está en inglés, como los nombres de la API; el agente te responde en tu idioma. ## Instalar Con la [CLI de skills](https://skills.sh) (Node.js 22.20 o posterior): ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} npx skills add https://docs.brelio.com/skill.md ``` Detecta tus agentes y te pregunta dónde instalarla. Para elegir: > [tab] Claude Code > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > npx skills add https://docs.brelio.com/skill.md -a claude-code -g > ``` > > O a mano: > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > mkdir -p ~/.claude/skills/brelio > curl -fsSL https://docs.brelio.com/skill.md -o ~/.claude/skills/brelio/SKILL.md > ``` > > Claude Code la carga cuando una tarea menciona Brelio. > [tab] Cursor > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > npx skills add https://docs.brelio.com/skill.md -a cursor > ``` > > La skill se instala en `.agents/skills/brelio/` del proyecto actual; añade `-g` para todos tus proyectos. > [tab] Codex > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > npx skills add https://docs.brelio.com/skill.md -a codex > ``` > > La skill se instala en `.agents/skills/brelio/` del proyecto actual; añade `-g` para todos tus proyectos. > [tab] Otros agentes > > Descarga el archivo en la carpeta donde tu agente lee las skills, como `brelio/SKILL.md`: > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > mkdir -p .agents/skills/brelio > curl -fsSL https://docs.brelio.com/skill.md -o .agents/skills/brelio/SKILL.md > ``` > [!NOTE] > > La skill no conecta tu agente con Brelio. Añade también el [servidor MCP](/es/quickstart/mcp) o crea una [clave de API](/es/quickstart/api). ## Mantenerla al día La skill cambia cuando cambia la API. Ejecuta `npx skills update` para actualizar la copia instalada, o vuelve a descargar [skill.md](https://docs.brelio.com/skill.md). # Inicio rápido: tu primera llamada a la API Source: https://docs.brelio.com/es/quickstart/api Crea una clave de API en Brelio y lista los servicios de tu clínica con un solo comando curl: tu primera llamada a la API en dos minutos. En dos minutos creas una clave de API de la organización y llamas a la API REST con ella. **1. Crea una clave de API** En Brelio, abre **Configuración › Integraciones › Claves de API** y pulsa **Nueva clave**. Ponle el nombre de la herramienta que la usará (por ejemplo «n8n»), marca **Ver servicios** y **Ver profesionales**, y créala. Copia la clave ahora: Brelio solo la muestra una vez. Trátala como una contraseña: guárdala en un gestor de secretos o en una variable de entorno, nunca en el código. > [!NOTE] > > Necesitas el permiso **Gestionar integraciones** en Brelio, y tu plan debe incluir el acceso a la API. **2. Guárdala en tu terminal** ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} export BRELIO_API_KEY="pega-aquí-tu-clave" ``` **3. Lista tus servicios** ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/services \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` La respuesta es una página de servicios: ```json Respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} { "data": [ { "id": "c3c8e275-a6b1-4fba-9b17-9c0a01e3ffc8", "name": "Fisioterapia", "description": null, "status": "active", "defaultDuration": 45, "color": null, "price": null, "createdAt": "2026-11-09T17:59:08.454Z", "updatedAt": "2026-11-09T17:59:08.454Z" } ], "next_cursor": null, "has_more": false } ``` **4. Mira los límites que lleva** Cada respuesta lleva las cabeceras de [límites de uso](/es/concepts/rate-limits): ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl -sS -D - -o /dev/null https://api.brelio.app/v1/services \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ```text Cabeceras de la respuesta theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} HTTP/2 200 ratelimit-policy: "burst";q=100;w=10, "standard";q=600;w=60 ratelimit: "standard";r=599;t=52 x-request-id: 64461250-2749-41ee-9ce9-1e01c2529401 ``` ## La misma llamada en tu lenguaje ```bash curl theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} curl https://api.brelio.app/v1/services?status=active \ -H "Authorization: Bearer $BRELIO_API_KEY" ``` ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} const response = await fetch('https://api.brelio.app/v1/services?status=active', { headers: { Authorization: `Bearer ${process.env.BRELIO_API_KEY}` }, }) if (!response.ok) throw new Error(JSON.stringify(await response.json())) const { data } = await response.json() for (const service of data) console.log(service.name, service.defaultDuration, 'min') ``` ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} import os import requests response = requests.get( "https://api.brelio.app/v1/services", params={"status": "active"}, headers={"Authorization": f"Bearer {os.environ['BRELIO_API_KEY']}"}, timeout=10, ) response.raise_for_status() for service in response.json()["data"]: print(service["name"], service["defaultDuration"], "min") ``` ## Si falla | Respuesta | Por qué | Solución | | - | - | - | | `401 PLATFORM_UNAUTHENTICATED` | Falta la cabecera `Authorization`, o la clave es incorrecta o está revocada | Envía `Authorization: Bearer ` con la clave completa. | | `402 PLATFORM_PLAN_REQUIRED` | El plan de la clínica no incluye el acceso a la API | Un propietario lo añade en **Configuración › Suscripción**. | | `403 OPERATION_FORBIDDEN` | A la clave le falta el permiso que indica `params.permission` | Crea una clave con ese permiso. | | `403 DATA_AREA_OFF` | La clínica no deja que las integraciones usen esa área de datos | Consulta las [áreas de datos](/es/concepts/data-areas). | Todos los códigos están explicados en [Errores](/es/concepts/errors). ## Siguiente paso > [card] [Reserva una cita](/es/guides/book-appointment) > > Busca un hueco libre, resérvalo, reprográmalo y cancélalo. > [card] [Autenticación](/es/authentication) > > Claves de la organización, claves personales, apps de partners y MCP. # Inicio rápido: conecta tu asistente de IA Source: https://docs.brelio.com/es/quickstart/mcp Añade el servidor MCP de Brelio a Claude, ChatGPT, Cursor, VS Code, Claude Code, Codex o Gemini CLI en dos minutos y pregunta por tu clínica. El servidor MCP permite que un asistente de IA lea los datos de tu clínica como tú, con exactamente tus permisos. No necesitas clave de API: el asistente te pide iniciar sesión con tu cuenta de Brelio y elegir la clínica. **Dirección del servidor:** `https://api.brelio.app/mcp` También la tienes, con botones de un clic, en Brelio en **Configuración › Conectar con IA**. ## 1. Añade el servidor a tu asistente > [tab] Claude > > **1. Abre los conectores** > > Abre [claude.ai › Customize › Connectors](https://claude.ai/customize/connectors) y pulsa **Add custom connector**. > > **2. Pega la dirección** > > Llámalo **Brelio**, pega `https://api.brelio.app/mcp` y pulsa **Add**. > > **3. Inicia sesión** > > Pulsa **Connect**, inicia sesión con tu cuenta de Brelio y permite el acceso. El conector queda disponible en Claude web, escritorio y móvil. > > > [!NOTE] > > > > En los planes Team y Enterprise, un propietario añade antes el conector en **Organization settings › Connectors**; después cada persona pulsa **Connect**. > [tab] ChatGPT > > **1. Abre los conectores** > > Abre [ChatGPT › Settings › Connectors](https://chatgpt.com/#settings/Connectors) y crea un conector. > > **2. Pega la dirección** > > Llámalo **Brelio**, usa `https://api.brelio.app/mcp` como URL del servidor y OAuth como autenticación. > > **3. Inicia sesión** > > Inicia sesión con tu cuenta de Brelio y permite el acceso. > > > [!NOTE] > > > > Tu plan y tu espacio de ChatGPT deben permitir conectores personalizados. > [tab] Cursor > > **Un clic:** [Añadir Brelio a Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=brelio\&config=eyJ1cmwiOiJodHRwczovL2FwaS5icmVsaW8uYXBwL21jcCJ9) > > O añádelo a `~/.cursor/mcp.json`: > > ```json mcp.json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > { > "mcpServers": { > "brelio": { "url": "https://api.brelio.app/mcp" } > } > } > ``` > > Cursor abre el navegador para iniciar sesión la primera vez que usa el servidor. > [tab] VS Code > > **Un clic:** [Añadir Brelio a VS Code](https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522brelio%2522%252C%2522type%2522%253A%2522http%2522%252C%2522url%2522%253A%2522https%253A%252F%252Fapi.brelio.app%252Fmcp%2522%257D) > > O ejecuta: > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > code --add-mcp '{"name":"brelio","type":"http","url":"https://api.brelio.app/mcp"}' > ``` > > VS Code te pide iniciar sesión cuando Copilot Chat arranca el servidor por primera vez. > [tab] Claude Code > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > claude mcp add --transport http brelio https://api.brelio.app/mcp > ``` > > Después, dentro de Claude Code, ejecuta `/mcp`, elige **brelio** y **Authenticate**. Añade `--scope user` para usarlo en todos tus proyectos. > [tab] Codex > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > codex mcp add brelio --url https://api.brelio.app/mcp > ``` > > Codex abre el navegador para iniciar sesión en ese momento. Si lo cierras, inicia sesión más tarde con `codex mcp login brelio`. > [tab] Gemini CLI > > ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} > gemini mcp add --scope user --transport http brelio https://api.brelio.app/mcp > ``` > > Después, dentro de Gemini CLI, ejecuta `/mcp auth brelio` para iniciar sesión. Gemini necesita un navegador en el mismo ordenador. Sin `--scope user`, el servidor se añade solo al proyecto actual. ## 2. Haz tu primera pregunta Pega este prompt en tu asistente: ```text Prompt theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}} Con las herramientas de Brelio: ¿qué centros y servicios tiene mi clínica, cuánto dura cada servicio y qué profesionales están activos? Responde con una tabla breve. ``` El asistente llama a `brelio_list_centers`, `brelio_list_services` y `brelio_list_professionals` y responde con los datos reales de tu clínica. Tienes más prompts en [Crea un agente con MCP](/es/guides/mcp-agent). > [!TIP] > > ¿Usas Claude Code, Cursor o Codex? Instala la [skill para agentes](/es/quickstart/agent-skill) para que tu agente conozca las reglas de Brelio: confirmar antes de escribir, pasar `centerId`, los códigos de error y los límites. ## Qué puede y qué no puede hacer tu asistente * Actúa **como tú**, en la clínica que elegiste al conectarlo. Nunca ve más de lo que ves tú en Brelio. * Solo ve las [herramientas](/es/mcp/tools) que tus permisos permiten. * En esta versión el servidor MCP ofrece **solo herramientas de lectura**. Para reservar, reprogramar o cancelar desde código, usa la [API REST](/es/quickstart/api). * Las herramientas de pacientes solo funcionan si la clínica ha activado el [área de datos](/es/concepts/data-areas) de pacientes y has aceptado el acceso a datos de pacientes para esta conexión. Si no lo has hecho, el asistente te muestra un enlace para aceptarlo. > [!WARNING] > > **Limitación conocida (noviembre de 2026).** Las herramientas de agenda, huecos libres, citas y pacientes todavía no aparecen en las conexiones MCP; solo aparecen las de equipo, servicios y configuración. Consulta las [limitaciones conocidas](/es/changelog#limitaciones-conocidas). ## Desconectar En Brelio, abre **Configuración › Conectar con IA › Tus conexiones** y pulsa **Desconectar**. El acceso del asistente se corta en cinco minutos como máximo. # Operaciones de la API y el MCP Source: https://docs.brelio.com/es/reference/operations Cada operación de la API de Brelio: su ruta REST, su herramienta MCP, el permiso y el área de datos que necesita. Generada desde el catálogo de operaciones: no la edites a mano. Una operación se ejecuta solo si la credencial tiene su permiso y el área de datos está activada en la clínica; las áreas de pacientes además necesitan aceptar el acceso a datos de pacientes. | Operación | REST | Herramienta MCP | permiso | área de datos | Datos | Riesgo | | - | - | - | - | - | - | - | | `agenda.list` | `GET /v1/agenda` | `brelio_list_agenda` | `calendar.read` | `agenda` | `pii` | Solo lectura | | `api_keys.list` | `GET /v1/api-keys` | `brelio_list_api_keys` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `api_keys.revoke` | `POST /v1/api-keys/{id}/revoke` | `brelio_revoke_api_keys` (solo REST) | `integrations.keys.manage` | `integrations` | `operational` | Pide confirmación antes de actuar | | `appointments.cancel` | `POST /v1/appointments/{id}/cancel` | `brelio_cancel_appointments` (solo REST) | `appointments.cancel` | `agenda` | `pii` | Pide confirmación antes de actuar | | `appointments.create` | `POST /v1/appointments` | `brelio_create_appointments` (solo REST) | `appointments.create` | `agenda` | `pii` | Escribe datos | | `appointments.get` | `GET /v1/appointments/{id}` | `brelio_get_appointments` | `appointments.read` | `agenda` | `pii` | Solo lectura | | `appointments.reschedule` | `POST /v1/appointments/{id}/reschedule` | `brelio_reschedule_appointments` (solo REST) | `appointments.reschedule` | `agenda` | `pii` | Escribe datos | | `availability.search` | `GET /v1/availability` | `brelio_search_availability` | `calendar.read` | `agenda` | `operational` | Solo lectura | | `centers.list` | `GET /v1/centers` | `brelio_list_centers` | `organization.centers.read` | `team` | `operational` | Solo lectura | | `me.api_keys.list` | `GET /v1/me/api-keys` | `brelio_list_me_api_keys` | `self` | `integrations` | `operational` | Solo lectura | | `me.connections.list` | `GET /v1/me/connections` | `brelio_list_me_connections` | `self` | `integrations` | `operational` | Solo lectura | | `partner_apps.list` | `GET /v1/partner-apps` | `brelio_list_partner_apps` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `partner_apps.revoke` | `POST /v1/partner-apps/{id}/revoke` | `brelio_revoke_partner_apps` (solo REST) | `integrations.keys.manage` | `integrations` | `operational` | Pide confirmación antes de actuar | | `patients.archive` | `POST /v1/patients/{id}/archive` | `brelio_archive_patients` (solo REST) | `patients.administrative.archive` | `patients` | `pii` | Pide confirmación antes de actuar | | `patients.create` | `POST /v1/patients` | `brelio_create_patients` (solo REST) | `patients.administrative.create` | `patients` | `pii` | Escribe datos | | `patients.get` | `GET /v1/patients/{id}` | `brelio_get_patients` | `patients.administrative.read` | `patients` | `pii` | Solo lectura | | `patients.list` | `GET /v1/patients` | `brelio_list_patients` | `patients.administrative.read` | `patients` | `pii` | Solo lectura | | `patients.update` | `PATCH /v1/patients/{id}` | `brelio_update_patients` (solo REST) | `patients.administrative.update` | `patients` | `pii` | Escribe datos | | `platform-policy.decisions.list` | `GET /v1/platform-policy/decisions` | `brelio_list_platform_policy_decisions` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `platform-policy.list` | `GET /v1/platform-policy` | `brelio_list_platform_policy` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `platform-policy.permissions.list` | `GET /v1/platform-policy/permissions` | — | `integrations.keys.manage` | `integrations` | `none` | Solo lectura | | `platform-policy.update` | `PATCH /v1/platform-policy/{id}` | — | `integrations.keys.manage` | `integrations` | `operational` | Escribe datos | | `professionals.list` | `GET /v1/professionals` | `brelio_list_professionals` | `professionals.read` | `team` | `operational` | Solo lectura | | `services.list` | `GET /v1/services` | `brelio_list_services` | `services.read` | `team` | `operational` | Solo lectura | | `webhooks.create` | `POST /v1/webhooks` | — | `integrations.keys.manage` | `integrations` | `operational` | Escribe datos | | `webhooks.delete` | `POST /v1/webhooks/{id}/delete` | `brelio_delete_webhooks` (solo REST) | `integrations.keys.manage` | `integrations` | `operational` | Pide confirmación antes de actuar | | `webhooks.eventTypes` | `GET /v1/webhook-event-types` | `brelio_list_webhook_event_types` | `integrations.keys.manage` | `integrations` | `none` | Solo lectura | | `webhooks.get` | `GET /v1/webhooks/{id}` | `brelio_get_webhooks` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `webhooks.list` | `GET /v1/webhooks` | `brelio_list_webhooks` | `integrations.keys.manage` | `integrations` | `operational` | Solo lectura | | `webhooks.redeliver` | `POST /v1/webhooks/{id}/redeliver` | `brelio_redeliver_webhooks` (solo REST) | `integrations.keys.manage` | `integrations` | `operational` | Escribe datos | | `webhooks.rotateSecret` | `POST /v1/webhooks/{id}/rotate-secret` | — | `integrations.keys.manage` | `integrations` | `operational` | Escribe datos | | `webhooks.test` | `POST /v1/webhooks/{id}/test` | `brelio_test_webhooks` (solo REST) | `integrations.keys.manage` | `integrations` | `operational` | Escribe datos |