VitrinaAPI

Precios de la clínica

Aranceles, reglas y convenios, y la cotización que explica cuánto le cuesta una prestación a un paciente concreto.

Beta
Puede cambiar en cualquier momento, con una entrada en el changelog y aviso a quienes la llamaron recientemente — ver versionado.

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étodoRutaQué hace
GET/clinic/labsList laboratorios
GET/clinic/pricing/agreementsList convenios
POST/clinic/pricing/agreementsCreate a convenio
DELETE/clinic/pricing/agreements/{id}Delete a convenio
PATCH/clinic/pricing/agreements/{id}Edit a convenio
GET/clinic/pricing/agreements/{id}/membersList a convenio’s members
POST/clinic/pricing/agreements/{id}/membersAdd a member to a convenio
GET/clinic/pricing/listsList price lists (aranceles)
POST/clinic/pricing/listsCreate 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}/entriesList the prices inside one arancel
PUT/clinic/pricing/lists/{id}/entriesSet prices inside one arancel
DELETE/clinic/pricing/members/{id}Remove a convenio membership
POST/clinic/pricing/quoteQuote a prestación, with the explanation
GET/clinic/pricing/rulesList price rules
POST/clinic/pricing/rulesCreate 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.

ParámetroEnTipoObligatorioRestricciones
activequerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/labs \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (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"
    }
  ]
}

Responde: 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.

ParámetroEnTipoObligatorioRestricciones
activequerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/pricing/agreements \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (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
    }
  ]
}

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
organization_namestring | nullmáx. 200
organization_rutstring | nullmáx. 20
price_list_idstring | null
billing_modepatient \organization \split
valid_fromstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
activeboolean
notesstring | nullmá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"
  }'

Ejemplo de respuesta (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
  }
}

Responde: 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"

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
organization_namestring | nullmáx. 200
organization_rutstring | nullmáx. 20
price_list_idstring | null
billing_modepatient \organization \split
valid_fromstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
activeboolean
notesstring | nullmá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"
  }'

Ejemplo de respuesta (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
  }
}

Responde: 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"

Ejemplo de respuesta (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"
    }
  ]
}

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
clinic_patient_iduuid
member_numberstring | nullmáx. 60
valid_fromstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatró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"
  }'

Ejemplo de respuesta (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"
  }
}

Responde: 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.

ParámetroEnTipoObligatorioRestricciones
activequerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/pricing/lists \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (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
    }
  ]
}

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
currencystringmín. 3, máx. 3
valid_fromstringpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
location_idstring | null
is_defaultboolean
activeboolean
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
  }'

Ejemplo de respuesta (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
  }
}

Responde: 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"

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
currencystringmín. 3, máx. 3
valid_fromstringpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
location_idstring | null
is_defaultboolean
activeboolean
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"
  }'

Ejemplo de respuesta (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
  }
}

Responde: 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"

Ejemplo de respuesta (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"
    }
  ]
}

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
entriesobject[]
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
      }
    ]
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "upserted": 2,
    "removed": 0
  }
}

Responde: 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"

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
service_iduuid
contact_idstring | null
clinic_patient_idstring | null
location_idstring | null
professional_idstring | null
onstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
agreement_idstring | null
previsionstring | nullmáx. 120
loyalty_tier0_6 \6_12 \12_plus
pack_idstring | 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"
  }'

Ejemplo de respuesta (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"
    }
  }
}

Responde: 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.

ParámetroEnTipoObligatorioRestricciones
activequerytrue \falseno
agreement_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/clinic/pricing/rules \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (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"
    }
  ]
}

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
kindconvenio \prevision \campaign \
subject_kindservice \category \all
subject_idstring | null
agreement_idstring | null
previsionstring | nullmáx. 120
loyalty_tier0_6 \6_12 \12_plus
professional_idstring | null
modifierpercent_off \amount_off \override
valueinteger≥ 0, ≤ 1000000000
priorityinteger≥ 0, ≤ 10000
valid_fromstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
activeboolean
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
  }'

Ejemplo de respuesta (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"
  }
}

Responde: 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"

Responde: 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.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
kindconvenio \prevision \campaign \
subject_kindservice \category \all
subject_idstring | null
agreement_idstring | null
previsionstring | nullmáx. 120
loyalty_tier0_6 \6_12 \12_plus
professional_idstring | null
modifierpercent_off \amount_off \override
valueinteger≥ 0, ≤ 1000000000
priorityinteger≥ 0, ≤ 10000
valid_fromstring | nullpatrón ^\d{4}-\d{2}-\d{2}$
valid_tostring | nullpatrón ^\d{4}-\d{2}-\d{2}$
activeboolean
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
  }'

Ejemplo de respuesta (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"
  }
}

Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429

En esta página