---
name: brelio
description: Work with a clinic's data in Brelio through its MCP server (brelio_* tools) and its REST API at api.brelio.app/v1. Use when a task mentions Brelio, a brelio_* tool or api.brelio.app, or asks to read a clinic's agenda, free slots, patients, services, team or webhooks, or to book, reschedule or cancel an appointment.
license: MIT
metadata:
  source: https://docs.brelio.com/skill.md
  generated: Generated from Brelio's operation catalogue and scripts/platform/skill/SKILL.template.md by `bun run platform:docs`. Edit the template, not this file.
---

# Brelio

Brelio is the practice-management software of Spanish clinics (physiotherapy, psychology, dentistry). This skill teaches you to read and change a clinic's data safely, through two doors that share one catalogue of operations, one permission model and one error format:

- **MCP server** `https://api.brelio.app/mcp`: OAuth through the person's Brelio account. It acts as that person, in the clinic they chose when connecting, with their permissions. It serves **query tools only**.
- **REST API** `https://api.brelio.app/v1`: a Bearer API key or partner-app token. Every operation, writes included. The contract is `https://api.brelio.app/v1/openapi.json`.

Human docs: https://docs.brelio.com. This file is https://docs.brelio.com/skill.md; install or refresh it with `npx skills add https://docs.brelio.com/skill.md`.

## Ground rules

1. **Brelio is the only source of clinic facts.** Patient names, phones, appointment times, prices and ids come from a tool or endpoint answer, quoted as returned. When an answer does not contain a fact, say so and ask; a guessed patient, time or id is a wrong booking in a real clinic.
2. **Confirm every write with the person first.** Before any `POST` or `PATCH`, show what will change (patient, professional, service, center, local time) and wait for an explicit yes. Operations marked **Asks first** below (cancel, archive, revoke, delete) need that yes even when the person already asked for them in general terms.
3. **Keep clinical information out of Brelio writes.** Notes, diagnoses and symptoms do not belong in administrative fields such as a patient's contact data.
4. **Times are UTC ISO 8601.** Convert to the clinic's time zone (Europe/Madrid unless the clinic says otherwise) before showing them, and back to UTC before sending them.
5. **Branch on the error `code`, then follow its `hint`.** The `detail` text can change; the `code` cannot.

## Choosing MCP or REST

- Reading, inside an assistant connected to Brelio: use the `brelio_*` MCP tools.
- Changing data (booking, rescheduling, cancelling, patients, webhooks): use REST. The write tools in the reference below are marked REST only until the MCP server serves them.
- A tool missing from your tool list means the person lacks its permission, or the clinic's plan or settings exclude it. Say which permission the reference names; do not route around it.

## Before you call

- **Centers.** A clinic can have several centers. Call `brelio_list_centers` (`GET /v1/centers`) first and pass `centerId` explicitly to the agenda, free-slot and booking operations. An agenda call over MCP without `centerId` reads only the person's **default center**, so "the whole clinic's agenda" means one call per center. Booking and free-slot search answer `APPOINTMENT_CENTER_REQUIRED` / `AVAILABILITY_CENTER_REQUIRED` without it when the clinic has more than one center.
- **Ids come from reads.** Get `patientId` from the patient list, `professionalId` and `serviceId` from their lists, and a start time from free-slot search. Never build or reuse an id from memory.
- **Book only offered slots.** Search free slots (`brelio_search_availability`, `GET /v1/availability`) and book one of the returned start times.
- **Pages.** Lists return `data`, `next_cursor` and `has_more`. Pass `next_cursor` as `cursor` while `has_more` is true; stop as soon as you have what the task needs.

## Writing over REST

Every write carries two guards:

- **`Idempotency-Key` header** on every `POST` and `PATCH`: a UUID v4 generated once per intended change, before the first attempt, and reused for every retry of that same change. A new change gets a new key. Brelio remembers keys for 24 hours.
- **`expectedVersion` in the body** of every change to an existing record (the **Version** column below). Read the record, copy its `version` exactly as returned (string or number, opaque), and send it back as `expectedVersion`. On `409 VERSION_CONFLICT` (or `WEBHOOK_VERSION_CONFLICT`, `DATA_POLICY_VERSION_CONFLICT`): read the record again, check the change still makes sense, show the person what changed, and retry with the new version and a **new** idempotency key.

```bash
curl -X POST https://api.brelio.app/v1/appointments/$ID/reschedule \
  -H "Authorization: Bearer $BRELIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"expectedVersion": "2", "start": "2026-11-16T09:00:00Z"}'
```

Some writes return a secret once (a webhook's signing secret). Store it from the first answer: a replay returns the record without it.

## Data areas and patient data

Every operation belongs to one data area (`agenda`, `team`, `patients`, `integrations`, …) and runs only while the clinic has that area on for integrations. Patient areas need two decisions: the clinic turned the area on, and this credential or connection was accepted for patient data.

- `403 DATA_AREA_OFF` (`params.area`): the clinic keeps that area off. Tell the person an owner can turn it on in Brelio (**Configuración › Integraciones**). Do not retry.
- `403 PATIENT_DATA_CONSENT_REQUIRED`: this connection or key has not been accepted for patient data. Over MCP, give the person the link in `params.consent_url`; after they accept in Brelio, retry once.
- `409 PATIENT_DATA_TERMS_PENDING`: patient areas cannot be turned on yet. Nothing to retry.

## Errors

Errors are RFC 9457 problem objects (`application/problem+json`), and over MCP the same object comes back as a tool result with `isError: true`: `code`, `status`, `detail`, `hint`, `params`, `request_id`, and `retryAfterSeconds` on 429 and 503. Quote `request_id` when the person needs Brelio support.

| Code | Status | Fix |
| --- | --- | --- |
| `PLATFORM_UNAUTHENTICATED` | 401 | Missing, invalid, revoked or expired credential. Reconnect or use a valid key. |
| `OPERATION_FORBIDDEN` | 403 | The credential lacks `params.permission`. Tell the person; use a credential that has it. |
| `CENTER_CONTEXT_FORBIDDEN` | 403 | The `centerId` is not one of the caller's centers. Pick one from the centers list. |
| `DATA_AREA_OFF` | 403 | See data areas above. |
| `PATIENT_DATA_CONSENT_REQUIRED` | 403 | See data areas above. |
| `PLATFORM_PLAN_REQUIRED` | 402 | The clinic's plan lacks API access. An owner adds it in Brelio. |
| `OPERATION_INPUT_INVALID` | 422 | Fix each field in `params.issues` (path and code). |
| `IDEMPOTENCY_KEY_REQUIRED` | 400 | Add an `Idempotency-Key` header. |
| `OPERATION_IDEMPOTENCY_CONFLICT` | 409 | The key was used for a different request. New change, new key. |
| `OPERATION_IDEMPOTENCY_IN_PROGRESS` | 409 | Wait a moment, retry with the same key. |
| `VERSION_CONFLICT` | 409 | Read again, retry with the new `version` and a new key. |
| `APPOINTMENT_CENTER_REQUIRED`, `AVAILABILITY_CENTER_REQUIRED` | 422 | Send `centerId`. |
| `APPOINTMENT_OVERLAP_DETECTED` | 409 | The slot was taken. Search free slots again and offer another. |
| `APPOINTMENT_PROFESSIONAL_UNAVAILABLE` | 422 | Use a start time from free-slot search. |
| `APPOINTMENT_REFERENCE_NOT_FOUND`, `AVAILABILITY_REFERENCE_NOT_FOUND` | 404 | The patient, professional or service in `params.what` is wrong or not bookable. Read it again. |
| `APPOINTMENT_CANCEL_IN_BRELIO`, `APPOINTMENT_RECURRING_UNSUPPORTED` | 409, 422 | Not possible through the API. The person does it in Brelio. |
| `PATIENT_NOT_FOUND`, `APPOINTMENT_NOT_FOUND` | 404 | Search again; archived patients are not found. |
| `PLATFORM_REVEAL_FORBIDDEN` | 403 | The operation returns a secret, which MCP never hands over. Use REST or Brelio. |
| `RATE_LIMITED` | 429 | Wait `Retry-After` seconds, then retry with the same idempotency key. |
| `PLATFORM_LIMITS_UNAVAILABLE`, `PLATFORM_AUTH_UNAVAILABLE` | 503 | Retry after `Retry-After` seconds. |

Retry only 429, 503 and 500 (with backoff and the same idempotency key). Every other 4xx needs a changed request, credential or clinic setting first. An unknown code: act on its `status` and `hint`. Full list: https://docs.brelio.com/en/concepts/errors

## Rate limits

| Limit | Counts | Default |
| --- | --- | --- |
| `burst` | Requests per credential | 100 per 10 s |
| `standard` | Requests per clinic, all its credentials and assistants together | 600 per 60 s |
| `heavy` | Requests per clinic to heavy operations | 30 per 60 s |
| `patient_records` | Patient records returned to one credential | 2000 per 1 h |

Every authenticated answer carries `RateLimit-Policy` and `RateLimit` headers (`r` remaining, `t` seconds to reset). Slow down before `r` reaches 0; fetch only the patient records the task needs, since they count against the hourly ceiling and alert the clinic's owners when it is reached.

## Reference

Generated from Brelio's operation catalogue. **Version** marks the changes that need `expectedVersion`. **Risk**: read, write, or **asks first** (confirm explicitly, every time).

### MCP tools

Query tools the MCP server serves. `*` marks a required argument. Needs: permission · data area.

| Tool | What and when | Arguments | Needs |
| --- | --- | --- | --- |
| `brelio_list_agenda` | Reads the clinic's agenda: the appointments in a time window, with start and end, status, professional, service, patient and center. To see which appointments fall on a day or week, or a professional's or center's. At most 31 days per call; it returns no notes or names. | `cursor`, `limit`, `from`*, `to`*, `professionalId`, `centerId` | `calendar.read` · `agenda` |
| `brelio_list_api_keys` | Lists the organization's API keys, without their secret. To manage access for the organization's programs (integrations, scripts) without a person's session. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` |
| `brelio_get_appointments` | Returns one appointment: when it is, its status, professional, service, patient, center and version. Before rescheduling or cancelling an appointment, to read its current version; or to check its status. | `id`* | `appointments.read` · `agenda` |
| `brelio_search_availability` | Finds free slots to book a patient with a professional for a service: up to 50 start times, with the same rules as Brelio's agenda. Before creating an appointment, to offer times that can be booked without a conflict. Windows up to 31 days. | `professionalId`*, `patientId`, `serviceId`*, `centerId`, `from`*, `to`*, `durationMinutes` | `calendar.read` · `agenda` |
| `brelio_list_centers` | Lists the organization's centers. The default center has isDefault. To know the clinic's locations, or to get a center id before filtering by it. | `cursor`, `limit` | `organization.centers.read` · `team` |
| `brelio_list_me_api_keys` | Lists the person's API keys in this organization. For a person to manage their own keys and connections. An organization key cannot use it, and creating or revoking takes the person's own session. | `cursor`, `limit` | `self` · `integrations` |
| `brelio_list_me_connections` | Lists the apps and assistants the person authorized to act for them. For a person to manage their own keys and connections. An organization key cannot use it, and creating or revoking takes the person's own session. | `cursor`, `limit` | `self` · `integrations` |
| `brelio_list_partner_apps` | Lists the organization's partner (machine-to-machine) apps, without their secret. To manage access for the organization's programs (integrations, scripts) without a person's session. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` |
| `brelio_get_patients` | Returns one patient's administrative details: name, contact, birth date and status. When you already have the patient's id and need their contact details or status. | `id`* | `patients.administrative.read` · `patients` |
| `brelio_list_patients` | Lists the clinic's patients with their administrative details, sorted by last name. To find a patient by name, email or phone and get their id before reading their record or booking them. | `cursor`, `limit`, `search`, `status` | `patients.administrative.read` · `patients` |
| `brelio_list_platform_policy_decisions` | History of decisions about the clinic's data, newest first: who, when and under which terms. To audit who turned an area on or off, or who accepted patient data for a connection. | `cursor`, `limit`, `subjectType`, `subjectId` | `integrations.keys.manage` · `integrations` |
| `brelio_list_platform_policy` | Which data areas (schedule, team, patients, billing) the clinic allows assistants and integrations to use. When an operation answers DATA_AREA_OFF, to see what is off and tell the person. | — | `integrations.keys.manage` · `integrations` |
| `brelio_list_professionals` | Lists the clinic team's professionals and their status. To know who works at the clinic, or to get a professional's id before reading their agenda. | `cursor`, `limit`, `status` | `professionals.read` · `team` |
| `brelio_list_services` | Lists the clinic's services: name, duration, status and price. To know what can be booked, how long it takes or what it costs, or to get a service id. | `cursor`, `limit`, `status`, `eligiblePlanId`, `activePackageAssignments`, `search` | `services.read` · `team` |
| `brelio_list_webhook_event_types` | Lists the events a webhook can subscribe to. Before creating a webhook, to choose its events. | — | `integrations.keys.manage` · `integrations` |
| `brelio_get_webhooks` | Shows a webhook with its latest deliveries: response, attempts and next retry. To diagnose why a system is not receiving notices. | `id`* | `integrations.keys.manage` · `integrations` |
| `brelio_list_webhooks` | Lists the organization's webhooks, their status and since when they fail. To review which systems receive notices or to find a webhook id. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` |

### REST operations

Every operation, writes included. Writes need `Idempotency-Key`. `*` marks a required field; `id` is the path parameter.

| Route | What | Input | Needs | Risk | Version | MCP |
| --- | --- | --- | --- | --- | --- | --- |
| `GET /v1/agenda` | Reads the clinic's agenda: the appointments in a time window, with start and end, status, professional, service, patient and center. | `cursor`, `limit`, `from`*, `to`*, `professionalId`, `centerId` | `calendar.read` · `agenda` | read | — | `brelio_list_agenda` |
| `GET /v1/api-keys` | Lists the organization's API keys, without their secret. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_api_keys` |
| `POST /v1/api-keys/{id}/revoke` | Revokes an organization API key. A key may revoke only itself; revoking others takes a person's session. It cannot be undone. | `id`* | `integrations.keys.manage` · `integrations` | **asks first** | — | REST only |
| `POST /v1/appointments/{id}/cancel` | Cancels a one-off appointment. It cannot be undone through the API. | `id`*, `expectedVersion`*, `reason` | `appointments.cancel` · `agenda` | **asks first** | yes | REST only |
| `POST /v1/appointments` | Books a patient with a professional for a service, with the same rules as Brelio's agenda. | `patientId`*, `professionalId`*, `serviceId`*, `start`*, `durationMinutes`, `centerId` | `appointments.create` · `agenda` | write | — | REST only |
| `GET /v1/appointments/{id}` | Returns one appointment: when it is, its status, professional, service, patient, center and version. | `id`* | `appointments.read` · `agenda` | read | — | `brelio_get_appointments` |
| `POST /v1/appointments/{id}/reschedule` | Moves a one-off appointment to another time, with the same rules as Brelio's agenda. Recurring appointments are not supported. | `id`*, `expectedVersion`*, `start`*, `durationMinutes` | `appointments.reschedule` · `agenda` | write | yes | REST only |
| `GET /v1/availability` | Finds free slots to book a patient with a professional for a service: up to 50 start times, with the same rules as Brelio's agenda. | `professionalId`*, `patientId`, `serviceId`*, `centerId`, `from`*, `to`*, `durationMinutes` | `calendar.read` · `agenda` | read | — | `brelio_search_availability` |
| `GET /v1/centers` | Lists the organization's centers. The default center has isDefault. | `cursor`, `limit` | `organization.centers.read` · `team` | read | — | `brelio_list_centers` |
| `GET /v1/me/api-keys` | Lists the person's API keys in this organization. | `cursor`, `limit` | `self` · `integrations` | read | — | `brelio_list_me_api_keys` |
| `GET /v1/me/connections` | Lists the apps and assistants the person authorized to act for them. | `cursor`, `limit` | `self` · `integrations` | read | — | `brelio_list_me_connections` |
| `GET /v1/partner-apps` | Lists the organization's partner (machine-to-machine) apps, without their secret. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_partner_apps` |
| `POST /v1/partner-apps/{id}/revoke` | Deletes a partner app; its tokens stop working. An app may delete only itself; deleting others takes a person's session. It cannot be undone. | `id`* | `integrations.keys.manage` · `integrations` | **asks first** | — | REST only |
| `POST /v1/patients/{id}/archive` | Archives a patient: it no longer appears in lists or searches, but its history is kept. It is reversible on the web. | `id`*, `expectedVersion`* | `patients.administrative.archive` · `patients` | **asks first** | yes | REST only |
| `POST /v1/patients` | Registers a new patient with their administrative details and links them to a center. | `firstName`*, `lastName`, `birthDate`, `sex`, `email`, `phone`, `centerId` | `patients.administrative.create` · `patients` | write | — | REST only |
| `GET /v1/patients/{id}` | Returns one patient's administrative details: name, contact, birth date and status. | `id`* | `patients.administrative.read` · `patients` | read | — | `brelio_get_patients` |
| `GET /v1/patients` | Lists the clinic's patients with their administrative details, sorted by last name. | `cursor`, `limit`, `search`, `status` | `patients.administrative.read` · `patients` | read | — | `brelio_list_patients` |
| `PATCH /v1/patients/{id}` | Changes a patient's administrative details (name, contact, birth date). Only the fields sent change; null clears a field. | `id`*, `expectedVersion`*, `firstName`, `lastName`, `birthDate`, `sex`, `email`, `phone` | `patients.administrative.update` · `patients` | write | yes | REST only |
| `GET /v1/platform-policy/decisions` | History of decisions about the clinic's data, newest first: who, when and under which terms. | `cursor`, `limit`, `subjectType`, `subjectId` | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_platform_policy_decisions` |
| `GET /v1/platform-policy` | Which data areas (schedule, team, patients, billing) the clinic allows assistants and integrations to use. | — | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_platform_policy` |
| `GET /v1/platform-policy/permissions` | The permissions a key or app can carry, grouped by data area, with their risk and whether they touch patients. | — | `integrations.keys.manage` · `integrations` | read | — | REST only |
| `PATCH /v1/platform-policy/{id}` | Turns a data area off for the whole clinic. Turning one on only happens in Brelio. | `id`*, `enabled`*, `expectedVersion`*, `acceptTerms`, `termsVersion` | `integrations.keys.manage` · `integrations` | write | yes | REST only |
| `GET /v1/professionals` | Lists the clinic team's professionals and their status. | `cursor`, `limit`, `status` | `professionals.read` · `team` | read | — | `brelio_list_professionals` |
| `GET /v1/services` | Lists the clinic's services: name, duration, status and price. | `cursor`, `limit`, `status`, `eligiblePlanId`, `activePackageAssignments`, `search` | `services.read` · `team` | read | — | `brelio_list_services` |
| `POST /v1/webhooks` | Registers an https URL that receives signed notices when the chosen events happen. | `url`*, `description`, `eventTypes`* | `integrations.keys.manage` · `integrations` | write | — | REST only |
| `POST /v1/webhooks/{id}/delete` | Deletes a webhook and its delivery log; it stops receiving notices at once. | `id`*, `expectedVersion`* | `integrations.keys.manage` · `integrations` | **asks first** | yes | REST only |
| `GET /v1/webhook-event-types` | Lists the events a webhook can subscribe to. | — | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_webhook_event_types` |
| `GET /v1/webhooks/{id}` | Shows a webhook with its latest deliveries: response, attempts and next retry. | `id`* | `integrations.keys.manage` · `integrations` | read | — | `brelio_get_webhooks` |
| `GET /v1/webhooks` | Lists the organization's webhooks, their status and since when they fail. | `cursor`, `limit` | `integrations.keys.manage` · `integrations` | read | — | `brelio_list_webhooks` |
| `POST /v1/webhooks/{id}/redeliver` | Sends a delivery again now, with the same webhook-id so the receiver does not process it twice. | `id`*, `deliveryId`* | `integrations.keys.manage` · `integrations` | write | — | REST only |
| `POST /v1/webhooks/{id}/rotate-secret` | Replaces a webhook's signing secret; the previous one keeps signing for 24 hours. | `id`*, `expectedVersion`* | `integrations.keys.manage` · `integrations` | write | yes | REST only |
| `POST /v1/webhooks/{id}/test` | Sends a signed test notice to the webhook, whatever it subscribes to. | `id`* | `integrations.keys.manage` · `integrations` | write | — | REST only |
