Clinic budgets
Budgets with a folio, their lines with the price frozen, and the treatment plans that order what is to be done.
Download the full API pública projection: openapi.json.
Quote a plan and close the till explains this resource in prose, with runnable examples.
| Method | Path | What it does |
|---|---|---|
DELETE | /clinic/budget-items/{id} | Remove a line from a DRAFT |
PATCH | /clinic/budget-items/{id} | Edit a line of a DRAFT |
GET | /clinic/budgets | List presupuestos (the conversion funnel) |
POST | /clinic/budgets | Create a presupuesto (draft) |
DELETE | /clinic/budgets/{id} | Throw away a DRAFT |
GET | /clinic/budgets/{id} | One presupuesto, by UUID or folio |
PATCH | /clinic/budgets/{id} | Edit a DRAFT presupuesto |
POST | /clinic/budgets/{id}/accept | Accept it (STAFF path) |
POST | /clinic/budgets/{id}/accept-and-collect | Collect a payment and accept the lines it pays (the counter) |
POST | /clinic/budgets/{id}/items | Add a line, freezing its price |
GET | /clinic/budgets/{id}/lineage | Every version of one proposal |
POST | /clinic/budgets/{id}/reject | Record the patient declining |
GET | /clinic/budgets/{id}/render | The presupuesto as a PDF |
POST | /clinic/budgets/{id}/send | Send it to the patient |
POST | /clinic/budgets/{id}/supersede | Write a new version (Nueva versión) |
POST | /clinic/budgets/{id}/void | Void it — never DELETE |
POST | /clinic/budgets/from-mirror-plan | Create a presupuesto (draft) from a mirrored Dentalink plan |
GET | /clinic/budgets/mirror-plans | A patient's mirrored Dentalink plans, for «Crear desde Dentalink» |
GET | /clinic/budgets/summary | The COMERCIAL badge and funnel, counted by lineage |
GET | /clinic/patients/{id}/treatment-progress | A patient's treatment progress |
POST | /clinic/treatment-plan-items/{id}/status | Move one phase item along |
GET | /clinic/treatment-plans | List treatment plans, with phase progress |
POST | /clinic/treatment-plans | Create a treatment plan by hand |
DELETE | /clinic/treatment-plans/{id} | Delete a plan |
GET | /clinic/treatment-plans/{id} | One plan with its phases and progress |
PATCH | /clinic/treatment-plans/{id} | Edit a plan |
DELETE /clinic/budget-items/{id}
Remove a line from a DRAFT
Remove a line from a DRAFT
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 |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/budget-items/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/budget-items/{id}
Edit a line of a DRAFT
Edit a line of a DRAFT
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
label | string | — | mín. 1, máx. 200 |
quantity | integer | — | ≥ 1, ≤ 999 |
unit_price_clp | integer | — | ≥ 0, ≤ 9999999999 |
discount_clp | integer | — | ≥ 0, ≤ 9999999999 |
tax_treatment | afecto \ | exento \ | no_gravado |
phase | integer | — | ≥ 1, ≤ 99 |
note | string | null | — | máx. 500 |
sort_order | integer | — | ≥ 0, ≤ 999 |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/budget-items/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"quantity": 2
}'Example response (200)
{
"data": {
"id": "3a7d9767-0000-4000-8000-000000000006",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 2,
"unit_price_clp": 35000,
"discount_clp": 0,
"total_clp": 70000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 3,
"note": "Control final",
"sort_order": 2,
"created_at": "2026-09-22T22:21:15.470Z",
"updated_at": "2026-09-22T22:21:15.481Z"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets
List presupuestos (the conversion funnel)
Quotes newest first, with their lines, the patient and the professional. ?live=true is the working set (borrador + enviado); ?status= picks one column. A voided presupuesto stays listable — a correlative series with a hole in it cannot be audited. Every row carries its LINEAGE (lineage_id, version, lineage_size, reissued_from_id, reissued_by_id) and the patient's «visto» (first_viewed_at, last_viewed_at). ?group_by=lineage returns ONE row per lineage — its latest version that is neither a draft nor voided (a draft only when the lineage has nothing else) — and the other filters then read that version; ?lineage_id= lists one lineage's versions.
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 |
|---|---|---|---|---|
status | query | draft \ | sent \ | accepted \ |
live | query | true \ | false | no |
patient_id | query | uuid | no | |
contact_id | query | uuid | no | |
professional_id | query | uuid | no | |
location_id | query | uuid | no | |
treatment_plan_id | query | uuid | no | |
lineage_id | query | uuid | no | |
group_by | query | lineage | 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/budgets \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"items": [
{
"id": "81e4a552-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 1,
"display_id": "E-1",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": null,
"lineage_id": "81e4a552-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.369Z",
"updated_at": "2026-09-22T22:21:15.412Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000004",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.392Z",
"updated_at": "2026-09-22T22:21:15.392Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000005",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.410Z",
"updated_at": "2026-09-22T22:21:15.410Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100
}
],
"total": 1,
"page": 1,
"limit": 50
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets
Create a presupuesto (draft)
Allocates the E- folio and freezes each line's price through the quote resolver — the arancel, the convenio and the professional surcharge are read ONCE and stored with the explanation that produced them. unit_price_clp on a line is the human override; omit it and the resolver decides. Always lands in draft. With reissued_from_id it is a NEW VERSION of that presupuesto and follows exactly the rules of POST /clinic/budgets/\{id\}/supersede: the draft copies the predecessor's header and lines (accepted lines carried), the header fields given override the copied ones, and items are added after the copied lines.
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 | |
contact_id | string | null | — | |
professional_id | string | null | — | |
location_id | string | null | — | |
price_list_id | string | null | — | |
agreement_id | string | null | — | |
conversation_id | string | null | — | |
lead_id | string | null | — | |
treatment_plan_id | string | null | — | |
reissued_from_id | string | null | — | |
tax_treatment | afecto \ | exento \ | no_gravado |
valid_until | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
installments | integer | null | — | ≥ 1, ≤ 48 |
notes | string | null | — | máx. 2000 |
items | object[] | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"items": [
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"quantity": 4,
"phase": 1
},
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"quantity": 1,
"phase": 2,
"discount_clp": 5000
}
]
}'Example response (201)
{
"data": {
"id": "81e4a552-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 1,
"display_id": "E-1",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": null,
"lineage_id": "81e4a552-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.369Z",
"updated_at": "2026-09-22T22:21:15.412Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000004",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.392Z",
"updated_at": "2026-09-22T22:21:15.392Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000005",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.410Z",
"updated_at": "2026-09-22T22:21:15.410Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/budgets/{id}
Throw away a DRAFT
Only a draft. Anything that has been sent is voided (POST /clinic/budgets/\{id\}/void) and keeps its folio — ADR 0058 §10.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/budgets/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets/{id}
One presupuesto, by UUID or folio
\{id\} accepts a UUID or the folio (E-47).
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 |
|---|---|---|---|---|
id | path | any | yes |
curl https://api.vitrinadev.com/api/v1/clinic/budgets/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"id": "81e4a552-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 1,
"display_id": "E-1",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": null,
"lineage_id": "81e4a552-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.369Z",
"updated_at": "2026-09-22T22:21:15.412Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000004",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.392Z",
"updated_at": "2026-09-22T22:21:15.392Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000005",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.410Z",
"updated_at": "2026-09-22T22:21:15.410Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/budgets/{id}
Edit a DRAFT presupuesto
Header fields only, and only while the quote is a draft — a sent one answers 409 rather than silently ignoring the patch. Moving the sucursal moves the razón social with it.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
contact_id | string | null | — | |
professional_id | string | null | — | |
location_id | string | null | — | |
price_list_id | string | null | — | |
agreement_id | string | null | — | |
conversation_id | string | null | — | |
lead_id | string | null | — | |
treatment_plan_id | string | null | — | |
tax_treatment | afecto \ | exento \ | no_gravado |
valid_until | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
installments | integer | null | — | ≥ 1, ≤ 48 |
notes | string | null | — | máx. 2000 |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/budgets/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"notes": "Incluye los cuatro controles y la limpieza inicial"
}'Example response (200)
{
"data": {
"id": "81e4a552-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 1,
"display_id": "E-1",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": null,
"lineage_id": "81e4a552-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles y la limpieza inicial",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.369Z",
"updated_at": "2026-09-22T22:21:15.445Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000004",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.392Z",
"updated_at": "2026-09-22T22:21:15.392Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000005",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.410Z",
"updated_at": "2026-09-22T22:21:15.410Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/accept
Accept it (STAFF path)
Stamps accepted_via: "staff" — the route decides, never the body. Accepts every line still presented, or (#2448) only item_ids: status: "accepted" means AT LEAST ONE line accepted, and the lines not yet accepted stay presented — the patient link keeps working for them, staff can accept more later, and valid_until still applies to them. Opens an obligation_group of kind presupuesto (one per lineage) plus one obligation per NEWLY accepted line in the payments ledger, creates or grows the treatment plan, and moves the lead to «Aceptado». Idempotent: a call with nothing left to accept returns the same objects with accepted_now: false; accepted_item_ids names the lines this call accepted. An AGENT can reach neither this route nor the public one.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
item_ids | uuid[] | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/accept \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Example response (200)
{
"data": {
"budget": {
"id": "81e4a552-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 2,
"display_id": "E-2",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "accepted",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": "a067dce6-0000-4000-8000-000000000002",
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-22T22:21:15.544Z",
"accepted_at": "2026-09-22T22:21:16.040Z",
"accepted_via": "staff",
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.502Z",
"updated_at": "2026-09-22T22:21:16.048Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000007",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.515Z",
"updated_at": "2026-09-22T22:21:15.515Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000008",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.530Z",
"updated_at": "2026-09-22T22:21:15.530Z",
"service_name": "Limpieza dental"
}
],
"is_live": false,
"days_left": 100
},
"plan": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "proposed",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.285Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "scheduled",
"appointment_id": "d93e8eec-0000-4000-8000-000000000001",
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.337Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 1,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 0,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
},
"obligation_group_id": "a067dce6-0000-4000-8000-000000000002",
"obligation_ids": [
"b44936d0-0000-4000-8000-000000000002",
"b44936d0-0000-4000-8000-000000000003"
],
"accepted_now": true,
"accepted_item_ids": [
"3a7d9767-0000-4000-8000-000000000007",
"3a7d9767-0000-4000-8000-000000000008"
]
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/accept-and-collect
Collect a payment and accept the lines it pays (the counter)
Reception's one gesture (#2448): the lines in item_ids become accepted (accepted_via: "staff", stamped with the cashier), their obligations open in the presupuesto group, and the payment is recorded and allocated to exactly those obligations — in ONE transaction, so a refused payment or allocation leaves the lines presented. The payer is the budget's contact_id (409 payer_unknown without one) and the caja its location_id. Money beyond the lines' total stays as the patient's credit; less leaves the last line partly paid. A POS swipe is a tarjeta* instrument with source: "card_terminal". Requires BOTH clinic_money:write and payments:write. 409 nothing_to_accept when the lines are already accepted, expired / not_sent like accept. Idempotency-Key is stored on the payment. An AGENT cannot reach it.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
item_ids | uuid[] | yes | |
payment | object | yes |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/accept-and-collect \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"item_ids": [
"3a7d9767-0000-4000-8000-000000000007"
],
"payment": {
"instrument": "tarjeta_debito",
"source": "card_terminal",
"amount_clp": 112000
}
}'Example response (201)
{
"data": {
"budget": {
"id": "81e4a552-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 2,
"display_id": "E-2",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "accepted",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": "a067dce6-0000-4000-8000-000000000002",
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-22T22:21:15.544Z",
"accepted_at": "2026-09-22T22:21:16.040Z",
"accepted_via": "staff",
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.502Z",
"updated_at": "2026-09-22T22:21:16.048Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000007",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.515Z",
"updated_at": "2026-09-22T22:21:15.515Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000008",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.530Z",
"updated_at": "2026-09-22T22:21:15.530Z",
"service_name": "Limpieza dental"
}
],
"is_live": false,
"days_left": 100
},
"plan": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "proposed",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.285Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "scheduled",
"appointment_id": "d93e8eec-0000-4000-8000-000000000001",
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.337Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 1,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 0,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
},
"obligation_group_id": "a067dce6-0000-4000-8000-000000000002",
"obligation_ids": [
"b44936d0-0000-4000-8000-000000000002"
],
"accepted_now": true,
"accepted_item_ids": [
"3a7d9767-0000-4000-8000-000000000007"
],
"payment": {
"payment": {
"id": "9a7e0000-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"source": "card_terminal",
"provenance": "manual",
"instrument": "tarjeta_debito",
"amount_clp": 112000,
"paid_on": "2026-09-22",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"allocated_clp": 112000,
"unallocated_clp": 0
},
"applied": [
{
"obligationId": "b44936d0-0000-4000-8000-000000000002",
"amountClp": 112000,
"allocatedClp": 112000,
"outstandingClp": 0,
"expectedClp": 112000,
"state": "paid",
"label": "Control de ortodoncia"
}
],
"surplus_clp": 0,
"reasons": [
"payer_recorded_by_staff",
"allocation_staff_manual"
],
"replayed": false
}
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/items
Add a line, freezing its price
Resolves the price through the quote explainer and stores the ordered steps beside the number. An explicit unit_price_clp is recorded AS an override step, so "why does it say $38.000" stays answerable.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
clinic_service_id | string | null | — | |
pack_id | string | null | — | |
unit_price_clp | integer | null | — | ≥ 0, ≤ 9999999999 |
quantity | integer | — | ≥ 1, ≤ 999, por defecto 1 |
discount_clp | integer | — | ≥ 0, ≤ 9999999999, por defecto 0 |
tax_treatment | afecto \ | exento \ | no_gravado |
phase | integer | — | ≥ 1, ≤ 99, por defecto 1 |
note | string | null | — | máx. 500 |
label | string | — | mín. 1, máx. 200 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/items \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"quantity": 1,
"phase": 3,
"note": "Control final"
}'Example response (201)
{
"data": {
"id": "3a7d9767-0000-4000-8000-000000000006",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 0,
"total_clp": 35000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 3,
"note": "Control final",
"sort_order": 2,
"created_at": "2026-09-22T22:21:15.470Z",
"updated_at": "2026-09-22T22:21:15.470Z"
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets/{id}/lineage
Every version of one proposal
The budget's whole lineage (#2446), version ascending — the first presupuesto handed over plus each one that superseded it. \{id\} may name any member, by UUID or folio.
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 |
|---|---|---|---|---|
id | path | any | yes |
curl https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/lineage \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"items": [
{
"id": "81e4a552-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 2,
"display_id": "E-2",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "accepted",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": "a067dce6-0000-4000-8000-000000000002",
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-23T01:07:07.750Z",
"accepted_at": "2026-09-23T01:07:07.912Z",
"accepted_via": "staff",
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"first_viewed_at": null,
"last_viewed_at": null,
"created_at": "2026-09-23T01:07:07.683Z",
"updated_at": "2026-09-23T01:07:07.941Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000007",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": null,
"created_at": "2026-09-23T01:07:07.701Z",
"updated_at": "2026-09-23T01:07:07.911Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000008",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": null,
"created_at": "2026-09-23T01:07:07.718Z",
"updated_at": "2026-09-23T01:07:07.911Z",
"service_name": "Limpieza dental"
}
],
"is_live": false,
"days_left": 100,
"version": 1,
"lineage_size": 2,
"reissued_by_id": "458363ad-0000-4000-8000-000000000001"
},
{
"id": "458363ad-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 3,
"display_id": "E-3",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": "81e4a552-0000-4000-8000-000000000002",
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"first_viewed_at": null,
"last_viewed_at": null,
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000009",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "458363ad-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": "3a7d9767-0000-4000-8000-000000000007",
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-00000000000a",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "458363ad-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": "3a7d9767-0000-4000-8000-000000000008",
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100,
"version": 2,
"lineage_size": 2,
"reissued_by_id": null
}
],
"total": 2
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/reject
Record the patient declining
Record the patient declining
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
reason | string | null | — | máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/reject \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "El paciente prefiere esperar al próximo semestre"
}'Example response (200)
{
"data": {
"id": "90afb8c1-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 3,
"display_id": "E-3",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "90afb8c1-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "rejected",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-22T22:21:16.137Z",
"accepted_at": null,
"accepted_via": null,
"rejected_at": "2026-09-22T22:21:16.460Z",
"rejected_reason": "El paciente prefiere esperar al próximo semestre",
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:16.091Z",
"updated_at": "2026-09-22T22:21:16.459Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000009",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "90afb8c1-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:16.106Z",
"updated_at": "2026-09-22T22:21:16.106Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-00000000000a",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "90afb8c1-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:16.121Z",
"updated_at": "2026-09-22T22:21:16.121Z",
"service_name": "Limpieza dental"
}
],
"is_live": false,
"days_left": 100
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets/{id}/render
The presupuesto as a PDF
Renders on demand and returns application/pdf. It is immutable BECAUSE every figure on it is frozen in the budget and its lines (ADR 0057), not because the bytes were stored.
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 |
|---|---|---|---|---|
id | path | any | yes |
curl https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/render \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/send
Send it to the patient
Moves draft → sent FIRST (the freeze must be in force before the patient can read the figures), then delivers through the conversation with a tokenised accept link: WhatsApp while the window is open, email otherwise. A patient with neither is reported honestly (delivered: "none") and the quote still counts as sent — the clinic handed it over. Also puts the lead on «Presupuesto enviado» with the total as its value.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
conversation_id | string | null | — | |
resend | boolean | — | por defecto false |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/send \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Example response (200)
{
"data": {
"budget": {
"id": "81e4a552-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 2,
"display_id": "E-2",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "sent",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-22T22:21:15.544Z",
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"created_at": "2026-09-22T22:21:15.502Z",
"updated_at": "2026-09-22T22:21:16.026Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000007",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.515Z",
"updated_at": "2026-09-22T22:21:15.515Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000008",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "81e4a552-0000-4000-8000-000000000002",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.530Z",
"updated_at": "2026-09-22T22:21:15.530Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100
},
"delivered": "email",
"accept_url": "https://vitrinadev-api.atribu.app/api/v1/public/clinic/budget/<token>",
"reason": null
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/supersede
Write a new version (Nueva versión)
From sent, accepted, rejected or expired (409 otherwise, and 409 when a version already supersedes it). Creates a DRAFT copying the header and every line verbatim, in the same lineage. Lines already ACCEPTED are carried forward (carried_from_item_id, their original accepted_at), are immutable on the draft (409 carried_item), and open no second obligation when the new version is accepted — which reuses the predecessor's obligation group. Nothing is withdrawn until the draft is SENT: that voids a sent, rejected or expired predecessor with reason superseded in the same transaction. An ACCEPTED predecessor stays accepted until the new version is itself accepted, so its debt always has an accepted document. Discarding the draft leaves the predecessor untouched. Same rules as POST /clinic/budgets with reissued_from_id. A presupuesto photographed from a mirrored Dentalink plan (mirror set) is RE-PHOTOGRAPHED instead: accepted lines are still carried, every other line comes from the plan's live state, and the new version records that state — born without mirror.changed_at (409 MIRROR_LINES_UNMAPPED if the live plan has a prestación missing from 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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/supersede \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Example response (201)
{
"data": {
"id": "458363ad-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 3,
"display_id": "E-3",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "81e4a552-0000-4000-8000-000000000002",
"reissued_from_id": "81e4a552-0000-4000-8000-000000000002",
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "draft",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": null,
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": null,
"voided_by": null,
"voided_reason": null,
"first_viewed_at": null,
"last_viewed_at": null,
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000009",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "458363ad-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": "3a7d9767-0000-4000-8000-000000000007",
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-00000000000a",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "458363ad-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"accepted_at": "2026-09-23T01:07:07.912Z",
"carried_from_item_id": "3a7d9767-0000-4000-8000-000000000008",
"created_at": "2026-09-23T01:07:07.990Z",
"updated_at": "2026-09-23T01:07:07.990Z",
"service_name": "Limpieza dental"
}
],
"is_live": true,
"days_left": 100,
"version": 2,
"lineage_size": 2,
"reissued_by_id": null
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/{id}/void
Void it — never DELETE
Keeps the folio and stays listable (ADR 0058 §10). reason is required by the service AND by a database CHECK: "why did we withdraw 40 quotes this month" is only answerable if it was written down. An ACCEPTED presupuesto cannot be voided here, and the reason superseded is reserved — write a new version instead (POST /clinic/budgets/\{id\}/supersede).
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
reason | string | yes | mín. 1, máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/void \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Se emitió con el arancel equivocado"
}'Example response (200)
{
"data": {
"id": "e9cede9b-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"display_seq": 4,
"display_id": "E-4",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"lead_id": "8cb33b27-0000-4000-8000-000000000001",
"lineage_id": "e9cede9b-0000-4000-8000-000000000001",
"reissued_from_id": null,
"conversation_id": null,
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"location_id": "b1b1b1b1-0000-4000-8000-000000000002",
"price_list_id": null,
"agreement_id": null,
"legal_entity_id": null,
"status": "voided",
"subtotal_clp": 147000,
"discount_clp": 5000,
"total_clp": 142000,
"tax_treatment": "afecto",
"valid_until": "2026-12-31",
"installments": 3,
"notes": "Incluye los cuatro controles del primer semestre",
"obligation_group_id": null,
"rendered_document_id": null,
"created_by_user_id": null,
"sent_at": "2026-09-22T22:21:16.514Z",
"accepted_at": null,
"accepted_via": null,
"rejected_at": null,
"rejected_reason": null,
"expired_at": null,
"voided_at": "2026-09-22T22:21:16.806Z",
"voided_by": null,
"voided_reason": "Se emitió con el arancel equivocado",
"created_at": "2026-09-22T22:21:16.473Z",
"updated_at": "2026-09-22T22:21:16.805Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-00000000000b",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "e9cede9b-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"pack_id": null,
"label": "Control de ortodoncia",
"quantity": 4,
"unit_price_clp": 28000,
"discount_clp": 0,
"total_clp": 112000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 28000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 1,
"note": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:16.485Z",
"updated_at": "2026-09-22T22:21:16.485Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-00000000000c",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"budget_id": "e9cede9b-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"pack_id": null,
"label": "Limpieza dental",
"quantity": 1,
"unit_price_clp": 35000,
"discount_clp": 5000,
"total_clp": 30000,
"price_explain": [
{
"kind": null,
"label": "Arancel particular 2026",
"order": 1,
"value": null,
"source": "price_list",
"rule_id": null,
"modifier": null,
"delta_clp": 0,
"amount_after_clp": 35000,
"amount_before_clp": null
}
],
"tax_treatment": "afecto",
"phase": 2,
"note": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:16.500Z",
"updated_at": "2026-09-22T22:21:16.500Z",
"service_name": "Limpieza dental"
}
],
"is_live": false,
"days_left": 100
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/budgets/from-mirror-plan
Create a presupuesto (draft) from a mirrored Dentalink plan
On a Dentalink clinic the plan is born in Dentalink; this composes the presupuesto FROM its mirrored copy in one action. Every line of the plan becomes a line at Dentalink's net price (after its descuentos), frozen with an explanation that names the source; the plan's professional becomes the presupuesto's (per-line performers are not kept). The response carries mirror: \{plan_id, snapshot_at, changed_at\}. When the plan later changes in Dentalink — a line added, removed or repriced, even at the same total — mirror.changed_at is set and clinic_budget.mirror_changed fires; POST /clinic/budgets/\{id\}/supersede then writes a new version from the live plan. 409 MIRROR_LINES_UNMAPPED lists the prestaciones missing from the local catalogue (details.fix names the importer); 409 with details.reason for a plan whose patient is not linked, a plan with no lines yet, or one another presupuesto already photographs. Vitrina never writes to Dentalink.
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 |
|---|---|---|---|
mirror_plan_id | uuid | yes | |
clinic_patient_id | uuid | — | |
contact_id | string | null | — | |
location_id | string | null | — | |
conversation_id | string | null | — | |
lead_id | string | null | — | |
tax_treatment | afecto \ | exento \ | no_gravado |
valid_until | string | null | — | patrón ^\d{4}-\d{2}-\d{2}$ |
installments | integer | null | — | ≥ 1, ≤ 48 |
notes | string | null | — | máx. 2000 |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/budgets/from-mirror-plan \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"mirror_plan_id": "6d2c1f0a-0000-4000-8000-000000000001"
}'Example response (201)
{
"data": {
"id": "458363ad-0000-4000-8000-000000000009",
"display_id": "E-52",
"status": "draft",
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"contact_id": "1a73af9e-0000-4000-8000-000000000001",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"lineage_id": "458363ad-0000-4000-8000-000000000009",
"reissued_from_id": null,
"subtotal_clp": 150000,
"discount_clp": 0,
"total_clp": 150000,
"tax_treatment": "afecto",
"valid_until": "2026-10-23",
"mirror": {
"plan_id": "6d2c1f0a-0000-4000-8000-000000000001",
"snapshot_at": "2026-09-23T14:02:11.000Z",
"changed_at": null
},
"items": [
{
"id": "a1c0ffee-0000-4000-8000-000000000001",
"clinic_service_id": "5e5e5e5e-0000-4000-8000-000000000001",
"label": "Resina compuesta",
"quantity": 1,
"unit_price_clp": 100000,
"discount_clp": 0,
"total_clp": 100000,
"price_explain": [
{
"order": 1,
"source": "mirror_snapshot",
"label": "Precio del tratamiento en Dentalink",
"amount_before_clp": null,
"amount_after_clp": 120000,
"delta_clp": 0,
"external_id": 1,
"external_plan": 90412
},
{
"order": 2,
"source": "mirror_snapshot",
"label": "Descuento registrado en Dentalink",
"amount_before_clp": 120000,
"amount_after_clp": 100000,
"delta_clp": -20000,
"external_id": 1,
"external_plan": 90412
}
]
},
{
"id": "a1c0ffee-0000-4000-8000-000000000002",
"clinic_service_id": "5e5e5e5e-0000-4000-8000-000000000002",
"label": "Sellante",
"quantity": 1,
"unit_price_clp": 50000,
"discount_clp": 0,
"total_clp": 50000,
"price_explain": [
{
"order": 1,
"source": "mirror_snapshot",
"label": "Precio del tratamiento en Dentalink",
"amount_before_clp": null,
"amount_after_clp": 50000,
"delta_clp": 0,
"external_id": 2,
"external_plan": 90412
}
]
}
],
"version": 1,
"lineage_size": 1,
"reissued_by_id": null
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets/mirror-plans
A patient's mirrored Dentalink plans, for «Crear desde Dentalink»
The patient's plans in the espejo de ingresos (a Dentalink clinic's tratamientos, copied read-only), newest first, each with lines_count, unmapped_count (lines whose prestación is not in the local catalogue yet — the plan cannot be photographed until the catalog importer runs) and already_budgeted / budget_id (a non-voided presupuesto already photographs it). available: false means the workspace has no enabled Dentalink mirror, and items is empty.
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 |
|---|---|---|---|---|
patient_id | query | uuid | yes |
curl https://api.vitrinadev.com/api/v1/clinic/budgets/mirror-plans \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"available": true,
"items": [
{
"id": "6d2c1f0a-0000-4000-8000-000000000001",
"external_id": 90412,
"fecha": "2026-09-10",
"finalizado": false,
"bloqueado": false,
"total": 150000,
"deuda": 150000,
"clinic_professional_id": "18342d1b-0000-4000-8000-000000000001",
"professional_name": "Camila Rojas",
"lines_count": 2,
"unmapped_count": 0,
"already_budgeted": false,
"budget_id": null
}
],
"total": 1
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/budgets/summary
The COMERCIAL badge and funnel, counted by lineage
Counts LINEAGES, not documents: a proposal re-issued three times is one proposal, read at its latest version that is neither a draft nor voided. sent_and_live is the badge and counts on valid_until rather than on status, so a quote that lapsed twelve minutes ago is already out — the expiry sweep runs on a tick and the badge must not tell the clinic to chase a dead number. lineages / by_status are the funnel.
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/budgets/summary \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"sent_and_live": 0
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/patients/{id}/treatment-progress
A patient's treatment progress
Two sources, both reported: pack-backed progress where the clinic sells packs, and performed / unclosed / upcoming citas per service off the mirror for a clinic that does not (Medilink). Reports what was PERFORMED; it never invents the session count quoted at a consultation.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/clinic/patients/<id>/treatment-progress \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"patient": {
"id": "b1b0b8de-0000-4000-8000-000000000001",
"source": "native",
"nombre": "María José",
"apellidos": "Fuentes Lagos"
},
"today": "2026-09-22",
"timezone": "America/Santiago",
"packs": [
{
"purchase_id": "d376ca29-0000-4000-8000-000000000001",
"name": "Pack 4 controles de ortodoncia",
"pack_id": "6acd6212-0000-4000-8000-000000000001",
"source": "native",
"purchased_at": "2026-09-22T22:21:14.782Z",
"expires_at": "2027-04-20",
"frozen_from": null,
"frozen_to": null,
"freeze_count": 1,
"stored_status": "active",
"precio_clp": 96000,
"obligation_group_id": "a067dce6-0000-4000-8000-000000000001",
"balance": {
"session_count": 4,
"consumed": 0,
"reversed": 1,
"remaining": 4,
"over_consumed": false,
"frozen": false,
"date_passed": false,
"days_to_expiry": 210,
"effective_status": "active"
},
"last_session_at": "2026-09-22T22:21:14.813Z",
"next_session_due_after": null
}
],
"services": [],
"truncated": false
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/treatment-plan-items/{id}/status
Move one phase item along
pending → scheduled → performed (or cancelled). scheduled requires the cita it lands on. The PLAN's own status follows from its items, so nobody has to remember to close a plan whose last phase was performed.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
status | pending \ | scheduled \ | performed \ |
appointment_id | string | null | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/treatment-plan-items/<id>/status \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "scheduled",
"appointment_id": "d93e8eec-0000-4000-8000-000000000001"
}'Example response (200)
{
"data": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "proposed",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.285Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "scheduled",
"appointment_id": "d93e8eec-0000-4000-8000-000000000001",
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.337Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 1,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 0,
"scheduled": 4,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/treatment-plans
List treatment plans, with phase progress
Progress is DERIVED from the items (pending / scheduled / performed), never a stored counter — a counter drifts the first time a cita is cancelled. ?live=true is accepted + in_progress, the work still to do.
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 |
|---|---|---|---|---|
status | query | draft \ | proposed \ | accepted \ |
live | query | true \ | false | no |
patient_id | query | uuid | no | |
professional_id | query | uuid | 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/treatment-plans \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"items": [],
"total": 0,
"page": 1,
"limit": 50
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /clinic/treatment-plans
Create a treatment plan by hand
Accepting a presupuesto creates one automatically; this is the path for a plan that never had a quote.
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 | |
name | string | yes | mín. 1, máx. 200 |
diagnosis | string | null | — | máx. 2000 |
professional_id | string | null | — | |
items | object[] | — |
curl -X POST https://api.vitrinadev.com/api/v1/clinic/treatment-plans \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"items": [
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4
},
{
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1
}
]
}'Example response (201)
{
"data": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "draft",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.213Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 5,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 4,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /clinic/treatment-plans/{id}
Delete a plan
Delete a plan
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 |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/treatment-plans/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
GET /clinic/treatment-plans/{id}
One plan with its phases and progress
One plan with its phases and progress
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 |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/clinic/treatment-plans/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "draft",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.213Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 5,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 4,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /clinic/treatment-plans/{id}
Edit a plan
status: "abandoned" requires abandoned_reason — "why do plans die at phase 2" is only answerable if somebody wrote it.
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 |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | — | mín. 1, máx. 200 |
diagnosis | string | null | — | máx. 2000 |
professional_id | string | null | — | |
status | draft \ | proposed \ | accepted \ |
abandoned_reason | string | null | — | máx. 500 |
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/treatment-plans/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "proposed"
}'Example response (200)
{
"data": {
"id": "1ccffee2-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"source": "native",
"external_id": null,
"clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
"name": "Ortodoncia fija superior e inferior",
"diagnosis": "Apiñamiento anterosuperior moderado",
"professional_id": "18342d1b-0000-4000-8000-000000000001",
"status": "proposed",
"accepted_at": null,
"completed_at": null,
"abandoned_reason": null,
"created_at": "2026-09-22T22:21:15.213Z",
"updated_at": "2026-09-22T22:21:15.285Z",
"patient_name": "María José Fuentes Lagos",
"professional_name": "Ana Rojas Vidal",
"items": [
{
"id": "3a7d9767-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000001",
"phase": 1,
"quantity": 4,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 0,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Control de ortodoncia"
},
{
"id": "3a7d9767-0000-4000-8000-000000000003",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"treatment_plan_id": "1ccffee2-0000-4000-8000-000000000001",
"clinic_service_id": "4cf5bc59-0000-4000-8000-000000000002",
"phase": 2,
"quantity": 1,
"tooth_or_zone": null,
"status": "pending",
"appointment_id": null,
"budget_item_id": null,
"sort_order": 1,
"created_at": "2026-09-22T22:21:15.215Z",
"updated_at": "2026-09-22T22:21:15.215Z",
"service_name": "Limpieza dental"
}
],
"progress": {
"pending": 5,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 5,
"percent": 0,
"phases": [
{
"phase": 1,
"pending": 4,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 4,
"percent": 0
},
{
"phase": 2,
"pending": 1,
"scheduled": 0,
"performed": 0,
"cancelled": 0,
"active": 1,
"percent": 0
}
],
"current_phase": 1
}
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
Clinic agenda
A clinic's day: read the agenda, find free slots, book and move an appointment, with the same ids whichever system runs it. A connected app reads it with patient pseudonyms.
Clinic till
One shift at one branch: open it with its float, close it against a count and sign off the reconciliation.