Content-Type: application/problem+json and the same shape everywhere. Over MCP, the same object comes back as a tool result with isError: true.
{
"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."
}
| Field | Meaning |
|---|---|
code | Stable identifier. Branch on this, never on detail or title. |
status | The HTTP status, repeated. |
detail | What happened, in English, for logs and developers. It can change. |
hint | What to do about it, when there is something to do. |
params | Machine-readable details: the missing permission, the invalid fields, the id. Never a payload. Always present. |
request_id | Also in the x-request-id header. Quote it when you ask Brelio for help. |
retryAfterSeconds | On 429 and 503: seconds to wait. Also in Retry-After. |
Invalid input
422 OPERATION_INPUT_INVALID lists each problem by path and code, never echoing the values (they may be patient data):
{
"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" }
]
}
}
Retry or not
| Status | Retry? |
|---|---|
400, 401, 402, 403, 404, 422 | No. Fix the request, the credential or the clinic’s settings first. |
409 | Not as is. Read the record again (version conflicts), wait (idempotency in progress) or change the request. |
429, 503 | Yes, after Retry-After seconds, with the same Idempotency-Key. |
500 | Yes, with backoff and the same Idempotency-Key. |
Every code
These are the codes the platform and its operations raise today. An operation can also pass through a code from Brelio’s own rules (for example a plan limit), always in the same shape, with adetail that explains it: treat an unknown code by its status.
Authentication and access
| Code | Status | Meaning | Fix |
|---|---|---|---|
PLATFORM_UNAUTHENTICATED | 401 | No bearer credential, or it is invalid, revoked or expired. | Send Authorization: Bearer <credential> with a valid key or token. |
PLATFORM_AUTH_UNAVAILABLE | 503 | The credential could not be verified right now. | Retry after Retry-After (30 s). |
PLATFORM_ORGANIZATION_NOT_LINKED | 403 | The credential belongs to an organization or person without access to Brelio. | Use a credential created in a Brelio clinic you belong to. |
PLATFORM_ORGANIZATION_AMBIGUOUS | 403 | MCP only: you belong to several clinics and the connection names none. | Reconnect and choose the clinic. |
PLATFORM_PLAN_REQUIRED | 402 | The clinic’s plan does not include API access. | An owner adds it in Configuración › Suscripción. |
PLATFORM_PLAN_UNAVAILABLE | 503 | API access could not be confirmed right now. | Retry after 30 s. |
OPERATION_FORBIDDEN | 403 | The credential, or the person behind it, lacks the permission in params.permission. | Use a credential with that permission. See permissions. |
CENTER_CONTEXT_FORBIDDEN | 403 | The centerId sent is not one of the caller’s centers. | Send a center you can see, or omit it. |
DATA_AREA_OFF | 403 | The clinic does not let integrations use the operation’s data area (params.area). | An owner turns the area on in Brelio. See data areas. |
DATA_AREA_UNKNOWN | 403 | The operation has no data area, so no integration may run it. | Report it to Brelio. |
PATIENT_DATA_CONSENT_REQUIRED | 403 | Patient data was not accepted for this credential. Over MCP, params.consent_url links to the acceptance page. | The person (or the owner, for organization keys and apps) accepts it in Brelio, then retry. |
PLATFORM_REVEAL_FORBIDDEN | 403 | The operation returns a one-time secret, which MCP cannot hand to a person. | Call it over REST, or create the credential in Brelio. |
Requests
| Code | Status | Meaning | Fix |
|---|---|---|---|
PLATFORM_ROUTE_NOT_FOUND | 404 | No operation at this method and path. | Check GET /v1/openapi.json. |
MALFORMED_JSON_BODY | 400 | The body is not a JSON object. | Send a JSON object with Content-Type: application/json. |
OPERATION_INPUT_INVALID | 422 | The input does not match the operation; see params.issues. | Fix the listed fields. |
IDEMPOTENCY_KEY_REQUIRED | 400 | A write without a valid Idempotency-Key. | Send a UUID. See idempotency. |
OPERATION_IDEMPOTENCY_CONFLICT | 409 | The key was used before with a different request. | Use a new key for a new change. |
OPERATION_IDEMPOTENCY_IN_PROGRESS | 409 | The first request with this key is still running. | Wait and retry with the same key. |
RATE_LIMITED | 429 | A rate limit was reached. | Wait Retry-After seconds. |
PLATFORM_LIMITS_UNAVAILABLE | 503 | The limiter did not answer; patient-data operations fail closed. | Retry after 5 s. |
PLATFORM_INTERNAL | 500 | MCP only: an unexpected failure. Nothing about it is sent. | Retry later; quote request_id. |
500 with the x-request-id header.
Versions
| Code | Status | Meaning | Fix |
|---|---|---|---|
VERSION_CONFLICT | 409 | The patient or appointment changed since you read it. | Read it again and retry with its new version. See concurrency. |
Appointments and free slots
| Code | Status | Meaning | Fix |
|---|---|---|---|
APPOINTMENT_NOT_FOUND | 404 | No appointment with that id that you can see. | Take ids from GET /v1/agenda or the create response. |
APPOINTMENT_REFERENCE_NOT_FOUND | 404 | The patient, professional or service (params.what) does not exist or cannot be booked by you. Inactive patients cannot be booked. | Check the id with the matching list. |
APPOINTMENT_CENTER_REQUIRED | 422 | The clinic has several centers and the appointment needs one. | Send centerId. |
APPOINTMENT_OVERLAP_DETECTED | 409 | The time is no longer free for the professional. | Search free slots again and pick another. |
APPOINTMENT_PROFESSIONAL_UNAVAILABLE | 422 | The professional does not work at that time. | Use a slot from GET /v1/availability. |
APPOINTMENT_RECURRING_UNSUPPORTED | 422 | Recurring appointments cannot be changed through the API yet. | Change it in Brelio. |
APPOINTMENT_CANCEL_IN_BRELIO | 409 | The appointment already has a session (checked in or later), so it is cancelled in Brelio. | Cancel it in Brelio. |
APPOINTMENT_RESCHEDULE_NOT_ALLOWED | 409 | The appointment’s state does not allow moving it. | Read it; only scheduled appointments move. |
APPOINTMENT_INVALID_STATE_TRANSITION | 409 | The change is not allowed from the appointment’s current status. | Read it and check its status. |
AVAILABILITY_CENTER_REQUIRED | 422 | The clinic has several centers. | Send centerId. |
AVAILABILITY_REFERENCE_NOT_FOUND | 404 | The patient, professional or service (params.what) is not one you can book here. | Check the id. |
Patients
| Code | Status | Meaning | Fix |
|---|---|---|---|
PATIENT_NOT_FOUND | 404 | No patient with that id that you can see. Archived patients are not found. | Search with GET /v1/patients. |
PATIENT_ACTION_NOT_ALLOWED | 409 | The patient’s state does not allow the change (params.action: edit or archive). | Read the patient; change it in Brelio if needed. |
Credentials
| Code | Status | Meaning | Fix |
|---|---|---|---|
CREDENTIAL_NOT_FOUND | 404 | No key, app or connection with that id in the clinic. | List them first. |
CREDENTIAL_SESSION_REQUIRED | 403 | Creating credentials or revoking another one needs a person in Brelio. | Do it in Brelio; a credential may revoke only itself. |
CREDENTIAL_PERMISSION_NOT_HELD | 403 | You tried to give permissions you do not hold (params.permissions). | Ask someone who holds them. |
PLATFORM_CREDENTIALS_UNAVAILABLE | 503 | The identity provider did not answer. | Retry later. |
PLATFORM_CREDENTIALS_REFUSED | 422 | The identity provider refused the request. | Check the names and permissions sent. |
Data policy
| Code | Status | Meaning | Fix |
|---|---|---|---|
DATA_POLICY_VERSION_CONFLICT | 409 | The area changed since you read it. | Read GET /v1/platform-policy and resend with its version. |
DATA_POLICY_UNCHANGED | 409 | The area already has that state. | Nothing to do. |
DATA_POLICY_NEEDS_PERSON | 403 | The change must be recorded as decided by a person. | Use a personal credential or Brelio. |
DATA_AREA_ENABLE_IN_BRELIO | 403 | Integrations can only turn areas off. | An owner turns it on in Brelio. |
PATIENT_DATA_ACCEPT_IN_BRELIO | 403 | Patient data is accepted by a person in Brelio. | Accept it in Brelio. |
PATIENT_DATA_CREDENTIAL_NOT_YOURS | 403 | You can decide only for your own keys and connections. | Ask an owner. |
PATIENT_DATA_NOTHING_TO_REVOKE | 409 | There is no acceptance to revoke. | Nothing to do. |
PATIENT_DATA_TERMS_NOT_ACCEPTED | 422 | Turning a patient area on needs acceptTerms: true and the termsVersion read. | Send both. |
PATIENT_DATA_TERMS_OUTDATED | 409 | The terms changed since they were shown. | Show the current terms and send their version. |
PATIENT_DATA_TERMS_PENDING | 409 | The reviewed terms are not published yet. | Wait for them. |
Webhooks
| Code | Status | Meaning | Fix |
|---|---|---|---|
WEBHOOK_NOT_FOUND | 404 | No webhook with that id. | Check GET /v1/webhooks. |
WEBHOOK_VERSION_CONFLICT | 409 | The webhook changed since you read it. | Read it again and retry with its version. |
WEBHOOK_EVENT_TYPE_UNKNOWN | 422 | An event type in eventTypes does not exist. | Choose from GET /v1/webhook-event-types. |
WEBHOOK_DELIVERY_PENDING | 409 | That delivery is already queued. | Wait; GET /v1/webhooks/{id} shows when it runs. |
WEBHOOK_TEST_RATE_LIMITED | 429 | More than 5 tests in a minute for this webhook. | Wait a minute. |
WEBHOOK_NOTHING_TO_DO | 404 | The webhook is gone or already disabled. | Nothing to do. |