Prestaciones de la clínica
Qué hace la clínica: duración, precio de lista, requisitos de consentimiento y quién puede ejecutar cada prestación.
Descarga la proyección completa de la API pública: openapi.json.
Definir prestaciones y precios explica este recurso en prosa, con ejemplos ejecutables.
| Método | Ruta | Qué hace |
|---|---|---|
PUT | /clinic/booking-catalog | Choose what the agent and «Nueva cita» book against |
GET | /clinic/motivos | List the clinic’s motivos de atención |
PATCH | /clinic/motivos/{id} | Edit a motivo de atención |
PUT | /clinic/motivos/{id}/professionals | Choose who attends a motivo |
POST | /clinic/motivos/agent-bookable | Set which motivos the AI agent may book, in bulk |
POST | /clinic/motivos/resync | Resync motivos de atención from the clinic system |
GET | /clinic/service-categories | List the clinic's service categories |
POST | /clinic/service-categories | Create a service category (find-or-create, native only) |
DELETE | /clinic/service-categories/{id} | Delete a service category |
PATCH | /clinic/service-categories/{id} | Enable/disable a service category |
GET | /clinic/services | List the clinic's services |
POST | /clinic/services | Create a native service |
DELETE | /clinic/services/{id} | Delete a native service |
PATCH | /clinic/services/{id} | Edit a native service (or activar/desactivar) |
GET | /clinic/services/{id}/professionals | Who may perform a prestación |
PUT | /clinic/services/{id}/professionals | Set who may perform a prestación |
POST | /clinic/services/agent-bookable | Set which prestaciones the AI agent may book, in bulk |
GET | /clinic/services/agent-bookable-count | How many active prestaciones the AI agent may book |
GET | /clinic/services/aranceles | The clinic's vendor aranceles (price lists) |
PUT /clinic/booking-catalog
Choose what the agent and «Nueva cita» book against
motivos (the clinic system’s motivos de atención) or prestaciones (the price list). motivos needs at least one synced motivo.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
catalog | motivos \ | prestaciones | sí |
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/booking-catalog \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"catalog": "motivos"
}'Ejemplo de respuesta (200)
{
"data": {
"catalog": "motivos"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/motivos
List the clinic’s motivos de atención
The «motivos de atención» mirrored from Dentalink/Medilink — the clinic system’s own bookable visit kinds per especialidad, each with its length and the professionals who attend it. vendor_* is what the clinic system said at the last resync; duration_minutes / active / the professional set are the effective values, which follow it unless overrides names them. booking_catalog says whether the AI agent and «Nueva cita» book against these or against the prestaciones.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
curl https://api.vitrinadev.com/api/v1/clinic/motivos \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"booking_catalog": "motivos",
"product": "dentalink",
"last_synced_at": "2026-10-22T18:00:00.000Z",
"last_error": null,
"motivos": [
{
"id": "0194e7a0-1c2b-7d3e-8f40-5a6b7c8d9e01",
"tenant_id": "0194e7a0-0000-7000-8000-000000000001",
"source": "healthatom",
"external_id": "23",
"nombre": "DIAGNOSTICO INTEGRAL ODONTOLOGIA",
"especialidad": "General",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"vendor_active": true,
"active": true,
"agent_bookable": true,
"color": "#cccccc",
"agenda_online": false,
"overrides": [
"duration_minutes"
],
"synced_at": "2026-10-22T18:00:00.000Z",
"created_at": "2026-10-22T18:00:00.000Z",
"updated_at": "2026-10-22T18:05:00.000Z",
"professionals": [
{
"external_id": "8",
"nombre": "Catalina Lima",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"active": true
}
]
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/motivos/{id}
Edit a motivo de atención
A duration_minutes or active set here becomes an override: every later resync keeps it (and reports it). A length set to exactly the clinic system’s own figure is not an override. reset hands fields back to the clinic system. A length set here applies to every professional; otherwise a booking uses the professional’s own length from the clinic system.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
duration_minutes | integer | — | ≥ 5, ≤ 1440 |
active | boolean | — | |
agent_bookable | boolean | — | |
reset | duration_minutes \ | active \ | professionals[] |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/motivos/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"duration_minutes": 60
}'Ejemplo de respuesta (200)
{
"data": {
"id": "0194e7a0-1c2b-7d3e-8f40-5a6b7c8d9e01",
"tenant_id": "0194e7a0-0000-7000-8000-000000000001",
"source": "healthatom",
"external_id": "23",
"nombre": "DIAGNOSTICO INTEGRAL ODONTOLOGIA",
"especialidad": "General",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"vendor_active": true,
"active": true,
"agent_bookable": true,
"color": "#cccccc",
"agenda_online": false,
"overrides": [
"duration_minutes"
],
"synced_at": "2026-10-22T18:00:00.000Z",
"created_at": "2026-10-22T18:00:00.000Z",
"updated_at": "2026-10-22T18:05:00.000Z",
"professionals": [
{
"external_id": "8",
"nombre": "Catalina Lima",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"active": true
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /clinic/motivos/{id}/professionals
Choose who attends a motivo
Replaces the professionals who attend the motivo (by the clinic system’s professional id). An override: resyncs keep it until reset: ["professionals"].
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
professionals | string[] | sí |
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/motivos/<id>/professionals \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"professionals": [
"8",
"19"
]
}'Ejemplo de respuesta (200)
{
"data": {
"id": "0194e7a0-1c2b-7d3e-8f40-5a6b7c8d9e01",
"tenant_id": "0194e7a0-0000-7000-8000-000000000001",
"source": "healthatom",
"external_id": "23",
"nombre": "DIAGNOSTICO INTEGRAL ODONTOLOGIA",
"especialidad": "General",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"vendor_active": true,
"active": true,
"agent_bookable": true,
"color": "#cccccc",
"agenda_online": false,
"overrides": [
"duration_minutes"
],
"synced_at": "2026-10-22T18:00:00.000Z",
"created_at": "2026-10-22T18:00:00.000Z",
"updated_at": "2026-10-22T18:05:00.000Z",
"professionals": [
{
"external_id": "8",
"nombre": "Catalina Lima",
"vendor_duration_minutes": 45,
"duration_minutes": 60,
"active": true
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/motivos/agent-bookable
Set which motivos the AI agent may book, in bulk
«¿Ofrecer servicio?» — Vitrina’s own flag, never sent to the clinic system and never touched by a resync.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
value | boolean | sí | |
ids | uuid[] | sí |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/motivos/agent-bookable \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"value": false,
"ids": [
"0194e7a0-1c2b-7d3e-8f40-5a6b7c8d9e01"
]
}'Ejemplo de respuesta (200)
{
"data": {
"updated": 1
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/motivos/resync
Resync motivos de atención from the clinic system
Reads the clinic system now and merges it in: new motivos are added, the rest refresh, and every field the clinic changed in Vitrina (overrides) is KEPT and listed in kept with both values. Motivos the clinic system no longer lists land in removed (deactivated, not deleted). Synchronous — about one second per professional.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
curl -X POST https://api.vitrinadev.com/api/v1/clinic/motivos/resync \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"total": 32,
"synced_at": "2026-10-22T18:00:00.000Z",
"added": [
{
"id": null,
"nombre": "Blanqueamiento",
"especialidad": "General"
}
],
"updated": [],
"removed": [],
"kept": [
{
"id": "0194e7a0-1c2b-7d3e-8f40-5a6b7c8d9e01",
"nombre": "DIAGNOSTICO INTEGRAL ODONTOLOGIA",
"especialidad": "General",
"field": "duration_minutes",
"vendor": "45 min",
"vitrina": "60 min"
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/service-categories
List the clinic's service categories
List the clinic's service categories
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
enabled | query | true \ | false | no |
curl https://api.vitrinadev.com/api/v1/clinic/service-categories \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "795cf9e4-0000-4000-8000-000000000001",
"source": "native",
"nombre": "Ortodoncia",
"enabled": true,
"created_at": "2026-09-22T22:21:14.461Z",
"updated_at": "2026-09-22T22:21:14.461Z"
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/service-categories
Create a service category (find-or-create, native only)
Create a service category (find-or-create, native only)
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
nombre | string | sí | mín. 1, máx. 120 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/service-categories \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"nombre": "Ortodoncia"
}'Ejemplo de respuesta (201)
{
"data": {
"id": "795cf9e4-0000-4000-8000-000000000001",
"source": "native",
"nombre": "Ortodoncia",
"enabled": true,
"created_at": "2026-09-22T22:21:14.461Z",
"updated_at": "2026-09-22T22:21:14.461Z"
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/service-categories/{id}
Delete a service category
Delete a service category
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/service-categories/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Responde: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/service-categories/{id}
Enable/disable a service category
Enable/disable a service category
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
enabled | boolean | sí |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/service-categories/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": true
}'Ejemplo de respuesta (200)
{
"data": {
"id": "795cf9e4-0000-4000-8000-000000000001",
"source": "native",
"nombre": "Ortodoncia",
"enabled": true,
"created_at": "2026-09-22T22:21:14.461Z",
"updated_at": "2026-09-22T22:21:14.476Z"
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/services
List the clinic's services
The clinic's price list. search (min 2 chars) matches nombre, código or categoría accent-insensitively. active is OPTIONAL and omitting it returns every row. precio is the vendor LIST price before previsión/convenio — it is deliberately never shown to the AI agent. is_entry filters entry prestaciones, tri-state: true = entry (evaluación / diagnóstico), false = CONFIRMED treatment rows only, unclassified = nobody has said yet (is_entry IS NULL); omitted = every row. arancel (repeatable or comma-separated) keeps only rows priced in those vendor aranceles ('0' = «Arancel base»); each row carries arancel_external_id + arancel_name. categoria keeps one exact category. agent_bookable (yes | no) filters on the EFFECTIVE agent-bookable value: each row carries the explicit agent_bookable (null = undecided) and agent_bookable_effective (agent_bookable ?? settings.clinic_attendance.agent_bookable_default, unset default = true).
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
search | query | string | no | mín. 2, máx. 120 |
active | query | true \ | false | no |
review_state | query | pending \ | stale \ | draft \ |
is_entry | query | true \ | false \ | unclassified |
arancel | query | array | null | no | |
categoria | query | string | no | mín. 1, máx. 120 |
agent_bookable | query | yes \ | no | no |
sort | query | nombre \ | codigo \ | categoria \ |
dir | query | asc \ | desc | no |
page | query | integer | no | ≥ 1, por defecto 1 |
limit | query | integer | no | ≥ 1, ≤ 200, por defecto 50 |
curl https://api.vitrinadev.com/api/v1/clinic/services \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"data": [
{
"id": "4cf5bc59-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"nombre": "Control de ortodoncia",
"codigo": "ORT-CTRL",
"categoria": "Ortodoncia",
"tipo": null,
"precio": 25000,
"arancel": null,
"duration_minutes": 30,
"vendor_duration_minutes": null,
"review_state": "draft",
"reviewed_at": null,
"reviewed_by": null,
"is_telehealth": false,
"online_bookable": true,
"requires_consent_template_id": null,
"eligibility_min_age_years": null,
"eligibility_max_age_years": null,
"eligibility_sexo": null,
"eligibility_previsiones": null,
"active": true,
"synced_at": null
},
{
"id": "4cf5bc59-0000-4000-8000-000000000002",
"source": "native",
"external_id": null,
"nombre": "Limpieza dental",
"codigo": null,
"categoria": "Ortodoncia",
"tipo": null,
"precio": 35000,
"arancel": null,
"duration_minutes": 45,
"vendor_duration_minutes": null,
"review_state": "draft",
"reviewed_at": null,
"reviewed_by": null,
"is_telehealth": false,
"online_bookable": false,
"requires_consent_template_id": null,
"eligibility_min_age_years": null,
"eligibility_max_age_years": null,
"eligibility_sexo": null,
"eligibility_previsiones": null,
"active": true,
"synced_at": null
}
],
"total": 2,
"page": 1,
"limit": 50
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/services
Create a native service
Create a native service
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
nombre | string | sí | mín. 1, máx. 200 |
codigo | string | null | — | máx. 60 |
categoria | string | null | — | máx. 120 |
tipo | string | null | — | máx. 120 |
precio | integer | null | — | ≥ 0, ≤ 1000000000 |
duration_minutes | integer | null | — | ≥ 1, ≤ 1440 |
active | boolean | — | |
is_telehealth | boolean | — | |
online_bookable | boolean | — | |
is_entry | boolean | null | — | |
agent_bookable | boolean | null | — | |
lab_bearing | boolean | — | |
requires_consent_template_id | string | null | — | |
eligibility_min_age_years | integer | null | — | ≥ 0, ≤ 120 |
eligibility_max_age_years | integer | null | — | ≥ 0, ≤ 120 |
eligibility_sexo | F \ | M \ | otro |
eligibility_previsiones | array | null | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/services \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"nombre": "Control de ortodoncia",
"codigo": "ORT-CTRL",
"categoria": "Ortodoncia",
"precio": 25000,
"duration_minutes": 30,
"online_bookable": true
}'Ejemplo de respuesta (201)
{
"data": {
"id": "4cf5bc59-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"nombre": "Control de ortodoncia",
"codigo": "ORT-CTRL",
"categoria": "Ortodoncia",
"tipo": null,
"precio": 25000,
"arancel": null,
"duration_minutes": 30,
"vendor_duration_minutes": null,
"review_state": "draft",
"reviewed_at": null,
"reviewed_by": null,
"is_telehealth": false,
"online_bookable": true,
"requires_consent_template_id": null,
"eligibility_min_age_years": null,
"eligibility_max_age_years": null,
"eligibility_sexo": null,
"eligibility_previsiones": null,
"active": true,
"synced_at": null
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/services/{id}
Delete a native service
Delete a native service
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/services/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Responde: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/services/{id}
Edit a native service (or activar/desactivar)
Edit a native service (or activar/desactivar)
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
nombre | string | — | mín. 1, máx. 200 |
codigo | string | null | — | máx. 60 |
categoria | string | null | — | máx. 120 |
tipo | string | null | — | máx. 120 |
precio | integer | null | — | ≥ 0, ≤ 1000000000 |
duration_minutes | integer | null | — | ≥ 1, ≤ 1440 |
active | boolean | — | |
is_telehealth | boolean | — | |
online_bookable | boolean | — | |
is_entry | boolean | null | — | |
agent_bookable | boolean | null | — | |
lab_bearing | boolean | — | |
requires_consent_template_id | string | null | — | |
eligibility_min_age_years | integer | null | — | ≥ 0, ≤ 120 |
eligibility_max_age_years | integer | null | — | ≥ 0, ≤ 120 |
eligibility_sexo | F \ | M \ | otro |
eligibility_previsiones | array | null | — |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/services/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"precio": 28000
}'Ejemplo de respuesta (200)
{
"data": {
"id": "4cf5bc59-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"nombre": "Control de ortodoncia",
"codigo": "ORT-CTRL",
"categoria": "Ortodoncia",
"tipo": null,
"precio": 28000,
"arancel": null,
"duration_minutes": 30,
"vendor_duration_minutes": null,
"review_state": "draft",
"reviewed_at": null,
"reviewed_by": null,
"is_telehealth": false,
"online_bookable": true,
"requires_consent_template_id": null,
"eligibility_min_age_years": null,
"eligibility_max_age_years": null,
"eligibility_sexo": null,
"eligibility_previsiones": null,
"active": true,
"synced_at": null
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/services/{id}/professionals
Who may perform a prestación
The eligibility set, with each professional’s optional duration and price override. NO ROWS means "anyone may" — never "nobody may".
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
curl https://api.vitrinadev.com/api/v1/clinic/services/<id>/professionals \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "18342d1b-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"duration_minutes": 30,
"precio_clp": 28000,
"active": true,
"created_at": "2026-09-22T22:21:14.591Z",
"updated_at": "2026-09-22T22:21:14.591Z",
"professional_nombre": "Ana"
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /clinic/services/{id}/professionals
Set who may perform a prestación
Replaces the WHOLE set in one transaction. An empty array clears it, which means "anyone may". 409 duplicate_professional when the body names the same professional twice.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
professionals | object[] | sí |
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/services/<id>/professionals \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"professionals": [
{
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"duration_minutes": 30,
"precio_clp": 28000
}
]
}'Ejemplo de respuesta (200)
{
"data": [
{
"id": "18342d1b-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"duration_minutes": 30,
"precio_clp": 28000,
"active": true,
"created_at": "2026-09-22T22:21:14.591Z",
"updated_at": "2026-09-22T22:21:14.591Z",
"professional_nombre": "Ana"
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/services/agent-bookable
Set which prestaciones the AI agent may book, in bulk
Sets agent_bookable to value — true (the agent may offer and book it), false (reception-only: the agent knows it exists, tells the patient reception schedules it and never books it) or null (back to the tenant default) — on up to 500 ids, OR on every row matching all_matching (the same filters, in the same string form, as the GET /clinic/services query). Exactly one of the two. Works on ANY source — the flag is Vitrina-owned and never reaches the vendor. updated is how many rows matched; ids of another tenant match nothing.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
value | boolean | null | sí | |
ids | uuid[] | — | |
all_matching | ClinicServiceMatchFilter | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/services/agent-bookable \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"value": false,
"all_matching": {
"search": "control",
"active": "true",
"arancel": "0"
}
}'Ejemplo de respuesta (200)
{
"data": {
"updated": 37
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/services/agent-bookable-count
How many active prestaciones the AI agent may book
\{ bookable, reception_only, total \} over ACTIVE services, by the EFFECTIVE agent-bookable value (agent_bookable, or the tenant default settings.clinic_attendance.agent_bookable_default when the row is undecided). arancel (repeatable or comma-separated) counts only rows priced in those vendor aranceles.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
arancel | query | array | null | no |
curl https://api.vitrinadev.com/api/v1/clinic/services/agent-bookable-count \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"bookable": 12,
"reception_only": 383,
"total": 395
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/services/aranceles
The clinic's vendor aranceles (price lists)
One entry per distinct arancel_external_id in the mirrored catálogo, with total rows and active rows, largest active list first. Dentalink/Medilink keep one copy of every prestación per arancel; this is what the screen filters on and what tenant.settings.clinic_catalog_aranceles (the aranceles the agent quotes) is chosen from. Empty for Reservo / native catálogos.
Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.
curl https://api.vitrinadev.com/api/v1/clinic/services/aranceles \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"external_id": "0",
"name": "Arancel base",
"total": 412,
"active": 390
},
{
"external_id": "652",
"name": "Arancel convenio",
"total": 652,
"active": 640
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
Seguros de la clínica
Los seguros del paciente (varios, uno principal), la tabla de cobertura por plan y prestación, y la cobertura estimada y el copago en el presupuesto y en el estado de cuenta, también en PDF. La cobertura nunca es un descuento: los precios y convenios no cambian.
Plantillas y conservación
Los textos y las preguntas antes de que nadie los firme o los responda, y cuántos años se guarda cada tipo de documento. No nombran a ningún paciente, y por eso no son sensibles.