Clinic pricing
Price lists, rules and agreements, and the quote that explains what one service costs one patient.
Download the full API pública projection: openapi.json.
List services and their prices explains this resource in prose, with runnable examples.
| Method | Path | What it does |
|---|---|---|
GET | /clinic/labs | List laboratorios |
GET | /clinic/pricing/agreements | List convenios |
POST | /clinic/pricing/agreements | Create a convenio |
DELETE | /clinic/pricing/agreements/{id} | Delete a convenio |
PATCH | /clinic/pricing/agreements/{id} | Edit a convenio |
GET | /clinic/pricing/agreements/{id}/members | List a convenio’s members |
POST | /clinic/pricing/agreements/{id}/members | Add a member to a convenio |
GET | /clinic/pricing/lists | List price lists (aranceles) |
POST | /clinic/pricing/lists | Create a price list |
DELETE | /clinic/pricing/lists/{id} | Delete a price list |
PATCH | /clinic/pricing/lists/{id} | Edit a price list |
GET | /clinic/pricing/lists/{id}/entries | List the prices inside one arancel |
PUT | /clinic/pricing/lists/{id}/entries | Set prices inside one arancel |
DELETE | /clinic/pricing/members/{id} | Remove a convenio membership |
POST | /clinic/pricing/quote | Quote a prestación, with the explanation |
GET | /clinic/pricing/rules | List price rules |
POST | /clinic/pricing/rules | Create a price rule |
DELETE | /clinic/pricing/rules/{id} | Delete a price rule |
PATCH | /clinic/pricing/rules/{id} | Edit a price rule |
GET /clinic/labs
List laboratorios
The dental labs the clinic sends work to — imported from Dentalink (/laboratorios) by the migration importer, or native. Each row carries the lab's contact (address, phone) as the clinic recorded it. Disabled labs included unless ?active=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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
active | query | true \ | false | no |
curl https://api.vitrinadev.com/api/v1/clinic/labs \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "c0e57121-0000-4000-8000-0000000001ab",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "healthatom",
"external_id": "9",
"name": "Laboratorio Ejemplo",
"contact": {
"direccion": "Avenida Ejemplo 456",
"telefono": "+56 9 0000 0200"
},
"active": true,
"created_at": "2026-09-23T18:00:00.000Z",
"updated_at": "2026-09-23T18:00:00.000Z"
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/pricing/agreements
List convenios
Empresas, colegios and cajas with their validity, their own arancel (if any), who is billed, and how many members they have.
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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
active | query | true \ | false | no |
curl https://api.vitrinadev.com/api/v1/clinic/pricing/agreements \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "c0e57121-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Convenio Empresa Andina",
"organization_name": "Empresa Andina SpA",
"organization_rut": "76.543.210-3",
"price_list_id": null,
"billing_mode": "patient",
"valid_from": "2026-01-01",
"valid_to": null,
"active": true,
"notes": null,
"created_at": "2026-09-22T22:21:14.651Z",
"updated_at": "2026-09-22T22:21:14.651Z",
"member_count": 0
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/pricing/agreements
Create a convenio
Create a convenio
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | yes | mín. 1, máx. 160 |
organization_name | string | null | — | máx. 200 |
organization_rut | string | null | — | máx. 20 |
price_list_id | string | null | — | |
billing_mode | patient \ | organization \ | split |
valid_from | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
active | boolean | — | |
notes | string | null | — | máx. 2000 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/pricing/agreements \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Convenio Empresa Andina",
"organization_name": "Empresa Andina SpA",
"organization_rut": "76.543.210-3",
"billing_mode": "patient",
"valid_from": "2026-01-01"
}'Example response (201)
{
"data": {
"id": "c0e57121-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Convenio Empresa Andina",
"organization_name": "Empresa Andina SpA",
"organization_rut": "76.543.210-3",
"price_list_id": null,
"billing_mode": "patient",
"valid_from": "2026-01-01",
"valid_to": null,
"active": true,
"notes": null,
"created_at": "2026-09-22T22:21:14.651Z",
"updated_at": "2026-09-22T22:21:14.651Z",
"member_count": 0
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/pricing/agreements/{id}
Delete a convenio
Memberships and the rules that name it cascade.
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 DELETE https://api.vitrinadev.com/api/v1/clinic/pricing/agreements/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/pricing/agreements/{id}
Edit a convenio
Edit a convenio
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | mín. 1, máx. 160 |
organization_name | string | null | — | máx. 200 |
organization_rut | string | null | — | máx. 20 |
price_list_id | string | null | — | |
billing_mode | patient \ | organization \ | split |
valid_from | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
active | boolean | — | |
notes | string | null | — | máx. 2000 |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/pricing/agreements/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"notes": "Presentar credencial de la empresa"
}'Example response (200)
{
"data": {
"id": "c0e57121-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Convenio Empresa Andina",
"organization_name": "Empresa Andina SpA",
"organization_rut": "76.543.210-3",
"price_list_id": null,
"billing_mode": "patient",
"valid_from": "2026-01-01",
"valid_to": null,
"active": true,
"notes": "Presentar credencial de la empresa",
"created_at": "2026-09-22T22:21:14.651Z",
"updated_at": "2026-09-22T22:21:14.663Z",
"member_count": 0
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/pricing/agreements/{id}/members
List a convenio’s members
Each membership with the patient’s name and RUT, its member number and its OWN validity window — the window the loyalty tier is measured from.
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/pricing/agreements/<id>/members \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "6467baa3-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"member_number": "EA-0042",
"valid_from": null,
"valid_to": null,
"created_at": "2026-09-22T22:21:14.673Z",
"updated_at": "2026-09-22T22:21:14.673Z",
"patient_nombre": "María José",
"patient_apellidos": "Fuentes Lagos",
"patient_rut": "11.111.111-1"
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/pricing/agreements/{id}/members
Add a member to a convenio
Idempotent per (patient, convenio): re-adding re-opens the window on the existing row rather than stacking a second membership.
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
clinic_patient_id | uuid | yes | |
member_number | string | null | — | máx. 60 |
valid_from | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/pricing/agreements/<id>/members \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"member_number": "EA-0042"
}'Example response (201)
{
"data": {
"id": "6467baa3-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"member_number": "EA-0042",
"valid_from": null,
"valid_to": null,
"created_at": "2026-09-22T22:21:14.673Z",
"updated_at": "2026-09-22T22:21:14.673Z"
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/pricing/lists
List price lists (aranceles)
Every arancel with its validity window, its sucursal (null = the whole workspace) and how many prestaciones it prices. Retired lists included unless ?active=true — this screen manages the catalogue.
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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
active | query | true \ | false | no |
curl https://api.vitrinadev.com/api/v1/clinic/pricing/lists \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "38b62be4-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Arancel particular 2026",
"currency": "CLP",
"valid_from": "2026-09-22",
"valid_to": null,
"location_id": null,
"is_default": true,
"active": true,
"created_at": "2026-09-22T22:21:14.611Z",
"updated_at": "2026-09-22T22:21:14.611Z",
"entry_count": 0
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/pricing/lists
Create a price list
A dated arancel. is_default: true demotes the previous default in the same scope (tenant-wide, or that sucursal).
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | yes | mín. 1, máx. 160 |
currency | string | — | mín. 3, máx. 3 |
valid_from | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
location_id | string | null | — | |
is_default | boolean | — | |
active | boolean | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/pricing/lists \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Arancel particular 2026",
"currency": "CLP",
"is_default": true
}'Example response (201)
{
"data": {
"id": "38b62be4-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Arancel particular 2026",
"currency": "CLP",
"valid_from": "2026-09-22",
"valid_to": null,
"location_id": null,
"is_default": true,
"active": true,
"created_at": "2026-09-22T22:21:14.611Z",
"updated_at": "2026-09-22T22:21:14.611Z",
"entry_count": 0
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/pricing/lists/{id}
Delete a price list
Its entries cascade. A convenio pointing at it keeps its row with a null price_list_id.
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 DELETE https://api.vitrinadev.com/api/v1/clinic/pricing/lists/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/pricing/lists/{id}
Edit a price list
Edit a price list
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | mín. 1, máx. 160 |
currency | string | — | mín. 3, máx. 3 |
valid_from | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
location_id | string | null | — | |
is_default | boolean | — | |
active | boolean | — |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/pricing/lists/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"valid_from": "2026-01-01"
}'Example response (200)
{
"data": {
"id": "38b62be4-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"name": "Arancel particular 2026",
"currency": "CLP",
"valid_from": "2026-01-01",
"valid_to": null,
"location_id": null,
"is_default": true,
"active": true,
"created_at": "2026-09-22T22:21:14.611Z",
"updated_at": "2026-09-22T22:21:14.627Z",
"entry_count": 0
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/pricing/lists/{id}/entries
List the prices inside one arancel
List the prices inside one arancel
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/pricing/lists/<id>/entries \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "c9f37927-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"price_list_id": "38b62be4-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"precio_clp": 28000,
"created_at": "2026-09-22T22:21:14.637Z",
"updated_at": "2026-09-22T22:21:14.637Z"
},
{
"id": "c9f37927-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"price_list_id": "38b62be4-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"precio_clp": 35000,
"created_at": "2026-09-22T22:21:14.637Z",
"updated_at": "2026-09-22T22:21:14.637Z"
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /clinic/pricing/lists/{id}/entries
Set prices inside one arancel
Bulk upsert. precio_clp: null REMOVES the entry — "this list does not price it" is the absence of a row, and 0 means free (ADR 0058 §2).
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
entries | object[] | yes |
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/pricing/lists/<id>/entries \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"entries": [
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"precio_clp": 28000
},
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"precio_clp": 35000
}
]
}'Example response (200)
{
"data": {
"upserted": 2,
"removed": 0
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/pricing/members/{id}
Remove a convenio membership
Remove a convenio membership
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 DELETE https://api.vitrinadev.com/api/v1/clinic/pricing/members/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/pricing/quote
Quote a prestación, with the explanation
Resolves the price for a prestación on a date, for a patient (through their contact), at a sucursal, with a professional — and returns the ORDERED EXPLANATION: which arancel supplied the base, which professional override replaced it, and every rule that moved it, in the order it was applied. base_clp: null with unpriced_reason: "no_price_anywhere" means nothing prices it — which is not the same as free. deposit_policy is a READ of the payments ledger’s own rule (§3.6); this module never writes deposits. A READ that is a POST because its input is a structured body: clinic:read.
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
service_id | uuid | yes | |
contact_id | string | null | — | |
clinic_patient_id | string | null | — | |
location_id | string | null | — | |
professional_id | string | null | — | |
on | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
agreement_id | string | null | — | |
prevision | string | null | — | máx. 120 |
loyalty_tier | 0_6 \ | 6_12 \ | 12_plus |
pack_id | string | null | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/pricing/quote \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"service_id": "4cf5bc59-0000-4000-8000-000000000001",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002"
}'Example response (200)
{
"data": {
"service": {
"id": "4cf5bc59-0000-4000-8000-000000000001",
"nombre": "Control de ortodoncia",
"codigo": "ORT-CTRL",
"categoria": "Ortodoncia"
},
"currency": "CLP",
"on": "2026-09-22",
"price_list": {
"id": "38b62be4-0000-4000-8000-000000000001",
"name": "Arancel particular 2026",
"valid_from": "2026-01-01",
"valid_to": null
},
"inputs": {
"contact_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"agreement_ids": [
"c0e57121-0000-4000-8000-000000000001"
],
"prevision": "Fonasa",
"loyalty_tier": "6_12",
"pack_id": null
},
"base_clp": 28000,
"final_clp": 23800,
"unpriced_reason": null,
"explain": [
{
"order": 1,
"source": "price_list",
"label": "Arancel particular 2026",
"kind": null,
"rule_id": null,
"modifier": null,
"value": null,
"amount_before_clp": null,
"amount_after_clp": 28000,
"delta_clp": 0
},
{
"order": 2,
"source": "price_rule",
"label": "15% convenio Empresa Andina",
"kind": "convenio",
"rule_id": "0f400122-0000-4000-8000-000000000001",
"modifier": "percent_off",
"value": 15,
"amount_before_clp": 28000,
"amount_after_clp": 23800,
"delta_clp": -4200
}
],
"applied_rule_ids": [
"0f400122-0000-4000-8000-000000000001"
],
"deposit_policy": {
"amount_clp": null,
"due_hours": null,
"scope": null,
"reason": "no_policy"
}
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/pricing/rules
List price rules
The declarative modifiers, in application order (priority ascending). Every real-world discount — a convenio override, a previsión price, a campaign, a loyalty tier — is a row here rather than a code branch.
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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
active | query | true \ | false | no |
agreement_id | query | uuid | no |
curl https://api.vitrinadev.com/api/v1/clinic/pricing/rules \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "0f400122-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "15% convenio Empresa Andina",
"kind": "convenio",
"subject_kind": "all",
"subject_id": null,
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"prevision": null,
"loyalty_tier": null,
"professional_id": null,
"modifier": "percent_off",
"value": 15,
"priority": 10,
"valid_from": null,
"valid_to": null,
"active": true,
"created_at": "2026-09-22T22:21:14.689Z",
"updated_at": "2026-09-22T22:21:14.689Z"
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/pricing/rules
Create a price rule
400 subject_id_required for a service/category rule with no subject, and 400 subject_kind_specialty_not_supported until clinic_service.specialty_id lands — a rule that can never fire is refused rather than accepted and silently ignored.
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | yes | mín. 1, máx. 160 |
kind | convenio \ | prevision \ | campaign \ |
subject_kind | service \ | category \ | all |
subject_id | string | null | — | |
agreement_id | string | null | — | |
prevision | string | null | — | máx. 120 |
loyalty_tier | 0_6 \ | 6_12 \ | 12_plus |
professional_id | string | null | — | |
modifier | percent_off \ | amount_off \ | override |
value | integer | yes | ≥ 0, ≤ 1000000000 |
priority | integer | — | ≥ 0, ≤ 10000 |
valid_from | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
active | boolean | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/pricing/rules \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "15% convenio Empresa Andina",
"kind": "convenio",
"subject_kind": "all",
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"modifier": "percent_off",
"value": 15,
"priority": 10
}'Example response (201)
{
"data": {
"id": "0f400122-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "15% convenio Empresa Andina",
"kind": "convenio",
"subject_kind": "all",
"subject_id": null,
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"prevision": null,
"loyalty_tier": null,
"professional_id": null,
"modifier": "percent_off",
"value": 15,
"priority": 10,
"valid_from": null,
"valid_to": null,
"active": true,
"created_at": "2026-09-22T22:21:14.689Z",
"updated_at": "2026-09-22T22:21:14.689Z"
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/pricing/rules/{id}
Delete a price rule
Delete a price rule
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 DELETE https://api.vitrinadev.com/api/v1/clinic/pricing/rules/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/pricing/rules/{id}
Edit a price rule
Edit a price rule
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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | mín. 1, máx. 160 |
kind | convenio \ | prevision \ | campaign \ |
subject_kind | service \ | category \ | all |
subject_id | string | null | — | |
agreement_id | string | null | — | |
prevision | string | null | — | máx. 120 |
loyalty_tier | 0_6 \ | 6_12 \ | 12_plus |
professional_id | string | null | — | |
modifier | percent_off \ | amount_off \ | override |
value | integer | — | ≥ 0, ≤ 1000000000 |
priority | integer | — | ≥ 0, ≤ 10000 |
valid_from | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
valid_to | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
active | boolean | — |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/pricing/rules/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"priority": 20
}'Example response (200)
{
"data": {
"id": "0f400122-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "15% convenio Empresa Andina",
"kind": "convenio",
"subject_kind": "all",
"subject_id": null,
"agreement_id": "c0e57121-0000-4000-8000-000000000001",
"prevision": null,
"loyalty_tier": null,
"professional_id": null,
"modifier": "percent_off",
"value": 15,
"priority": 20,
"valid_from": null,
"valid_to": null,
"active": true,
"created_at": "2026-09-22T22:21:14.689Z",
"updated_at": "2026-09-22T22:21:14.701Z"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
Patient register
Who is a patient of the clinic and who answers for them: the identity record, the duplicates and the family bonds. Marked sensitive — it asks for its own scope and no connected app reaches it.
Clinic professionals
Who sees patients at the clinic, in which speciality and with what default block — the id the agenda uses as a lane.