VitrinaAPI

Clinic insurance

The patient's insurance (several, one primary), the coverage table per plan and prestación, and the estimated coverage and copago on the presupuesto and the estado de cuenta, in PDF too. Coverage is never a discount: prices and convenios do not change.

Beta
This may change at any time, with a changelog entry and a notice to recent callers — see versioning.

Download the full API pública projection: openapi.json.

MethodPathWhat it does
GET/clinic/budgets/{id}/coverageCobertura and copago of a presupuesto
GET/clinic/insurance/plansList the insurer plans
POST/clinic/insurance/plansAdd an insurer plan
PATCH/clinic/insurance/plans/{id}Rename or withdraw an insurer plan
GET/clinic/insurance/plans/{id}/coverageRead a plan’s coverage table
PUT/clinic/insurance/plans/{id}/coverageSet one coverage rule
DELETE/clinic/insurance/plans/{id}/coverage/{ruleId}Remove one coverage rule
GET/clinic/patients/{id}/insurance-statementEstado de cuenta with the seguro’s cobertura
GET/clinic/patients/{id}/insurance-statement/renderEstado de cuenta with the seguro, as a PDF
GET/clinic/patients/{id}/insurancesList a patient’s seguros
POST/clinic/patients/{id}/insurancesAdd a seguro to a patient
DELETE/clinic/patients/{id}/insurances/{insuranceId}Remove a patient’s seguro
PATCH/clinic/patients/{id}/insurances/{insuranceId}Edit a patient’s seguro

GET /clinic/budgets/{id}/coverage

Cobertura and copago of a presupuesto

Per line and in total: price_clp, the seguro’s estimated coverage_clp and the patient’s copay_clp (price = coverage + copay). Uses the patient’s primary seguro, or insurance_id as a what-if. status is covered, no_insurance or insurance_expired; without a usable seguro the copay is the full price. totals.price_clp is the presupuesto’s own total, so a presupuesto-level discount is already in it (budget_discount_clp). id is the budget id or its folio. Derived on read, never frozen; the same figures print on GET /clinic/budgets/\{id\}/render. Requires clinic_money: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.

ParameterInTypeRequiredConstraints
idpathanyyes
insurance_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/clinic/budgets/<id>/coverage \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "budget_id": "e0e57121-0000-4000-8000-0000000009ab",
    "status": "covered",
    "insurance": {
      "id": "c0e57121-0000-4000-8000-0000000007ad",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
      "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
      "insurer": "Consorcio",
      "plan_name": "Salud Plus",
      "plan_active": true,
      "policy_number": "P-48213",
      "holder_name": null,
      "valid_until": "2027-03-31",
      "is_primary": true,
      "created_at": "2026-10-09T17:45:00.000Z",
      "updated_at": "2026-10-09T17:45:00.000Z"
    },
    "lines": [
      {
        "item_id": "f0e57121-0000-4000-8000-0000000009a1",
        "label": "Limpieza dental",
        "quantity": 1,
        "price_clp": 40000,
        "coverage_clp": 28000,
        "copay_clp": 12000,
        "rule": "service",
        "capped": false
      },
      {
        "item_id": "f0e57121-0000-4000-8000-0000000009a2",
        "label": "Blanqueamiento",
        "quantity": 1,
        "price_clp": 120000,
        "coverage_clp": 0,
        "copay_clp": 120000,
        "rule": "none",
        "capped": false
      }
    ],
    "totals": {
      "price_clp": 160000,
      "coverage_clp": 28000,
      "copay_clp": 132000
    },
    "budget_discount_clp": 0
  }
}

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

GET /clinic/insurance/plans

List the insurer plans

The seguros the clinic keeps a coverage table for, active first, each with its rule_count. active=true is what the patient form offers. Requires 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.

ParameterInTypeRequiredConstraints
activequerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/insurance/plans \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "items": [
      {
        "id": "c0e57121-0000-4000-8000-0000000007ab",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "insurer": "Consorcio",
        "name": "Salud Plus",
        "active": true,
        "rule_count": 2,
        "created_at": "2026-10-01T13:00:00.000Z",
        "updated_at": "2026-10-01T13:00:00.000Z"
      }
    ],
    "total": 1
  }
}

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

POST /clinic/insurance/plans

Add an insurer plan

insurer and name are unique together per clinic, ignoring case (409 otherwise). Requires clinic_admin:write.

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

FieldTypeRequiredConstraints
insurerstringyesmín. 1, máx. 120
namestringyesmín. 1, máx. 120
curl -X POST https://api.vitrinadev.com/api/v1/clinic/insurance/plans \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "insurer": "Consorcio",
    "name": "Salud Plus"
  }'

Example response (201)

{
  "data": {
    "id": "c0e57121-0000-4000-8000-0000000007ab",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "insurer": "Consorcio",
    "name": "Salud Plus",
    "active": true,
    "rule_count": 2,
    "created_at": "2026-10-01T13:00:00.000Z",
    "updated_at": "2026-10-01T13:00:00.000Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

PATCH /clinic/insurance/plans/{id}

Rename or withdraw an insurer plan

active: false withdraws it from the patient form; patients who hold it keep it and it keeps covering. Plans are never deleted. Requires clinic_admin:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
insurerstring—mín. 1, máx. 120
namestring—mín. 1, máx. 120
activeboolean—
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/insurance/plans/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "active": false
  }'

Example response (200)

{
  "data": {
    "id": "c0e57121-0000-4000-8000-0000000007ab",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "insurer": "Consorcio",
    "name": "Salud Plus",
    "active": false,
    "rule_count": 2,
    "created_at": "2026-10-01T13:00:00.000Z",
    "updated_at": "2026-10-01T13:00:00.000Z"
  }
}

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

GET /clinic/insurance/plans/{id}/coverage

Read a plan’s coverage table

The default row first, then one row per prestación by name. A percent row carries percent (0 to 100); an amount row carries amount_clp per unit; either may carry a cap_clp per line. Requires 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.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/clinic/insurance/plans/<id>/coverage \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "items": [
      {
        "id": "c0e57121-0000-4000-8000-0000000007ac",
        "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
        "clinic_service_id": "d0e57121-0000-4000-8000-0000000008ab",
        "service_name": "Limpieza dental",
        "kind": "percent",
        "percent": 70,
        "amount_clp": null,
        "cap_clp": 30000
      }
    ],
    "total": 1
  }
}

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

PUT /clinic/insurance/plans/{id}/coverage

Set one coverage rule

Creates or replaces the plan’s row for clinic_service_id (omit it or send null for the plan default). kind: percent takes percent and no amount; kind: amount takes amount_clp and no percent (400 otherwise). Requires clinic_admin:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
clinic_service_idstring | null—
kindpercent \amountyes
percentnumber | null—≥ 0, ≤ 100
amount_clpinteger | null—≥ 0, ≤ 1000000000
cap_clpinteger | null—≥ 0, ≤ 1000000000
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/insurance/plans/<id>/coverage \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clinic_service_id": "d0e57121-0000-4000-8000-0000000008ab",
    "kind": "percent",
    "percent": 70,
    "cap_clp": 30000
  }'

Example response (200)

{
  "data": {
    "id": "c0e57121-0000-4000-8000-0000000007ac",
    "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
    "clinic_service_id": "d0e57121-0000-4000-8000-0000000008ab",
    "service_name": "Limpieza dental",
    "kind": "percent",
    "percent": 70,
    "amount_clp": null,
    "cap_clp": 30000
  }
}

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

DELETE /clinic/insurance/plans/{id}/coverage/{ruleId}

Remove one coverage rule

The prestación falls back to the plan default, or to no cover. Requires clinic_admin:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes
ruleIdpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/insurance/plans/<id>/coverage/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429

GET /clinic/patients/{id}/insurance-statement

Estado de cuenta with the seguro’s cobertura

The patient’s accepted presupuestos, oldest first, each with price, cobertura and copago per line and in total, and a grand total. A reissued version replaces the one it carries lines from. Payments and the outstanding balance stay the ledger’s answer (GET /contacts/\{id\}/ledger). Requires clinic_money: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.

ParameterInTypeRequiredConstraints
idpathuuidyes
insurance_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurance-statement \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "patient_name": "Paciente de ejemplo",
    "status": "covered",
    "insurance": {
      "id": "c0e57121-0000-4000-8000-0000000007ad",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
      "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
      "insurer": "Consorcio",
      "plan_name": "Salud Plus",
      "plan_active": true,
      "policy_number": "P-48213",
      "holder_name": null,
      "valid_until": "2027-03-31",
      "is_primary": true,
      "created_at": "2026-10-09T17:45:00.000Z",
      "updated_at": "2026-10-09T17:45:00.000Z"
    },
    "budgets": [
      {
        "budget_id": "e0e57121-0000-4000-8000-0000000009ab",
        "display_id": "E-12",
        "status": "accepted",
        "accepted_at": "2026-10-09T18:00:00.000Z",
        "lines": [
          {
            "item_id": "f0e57121-0000-4000-8000-0000000009a1",
            "label": "Limpieza dental",
            "quantity": 1,
            "price_clp": 40000,
            "coverage_clp": 28000,
            "copay_clp": 12000,
            "rule": "service",
            "capped": false
          },
          {
            "item_id": "f0e57121-0000-4000-8000-0000000009a2",
            "label": "Blanqueamiento",
            "quantity": 1,
            "price_clp": 120000,
            "coverage_clp": 0,
            "copay_clp": 120000,
            "rule": "none",
            "capped": false
          }
        ],
        "totals": {
          "price_clp": 160000,
          "coverage_clp": 28000,
          "copay_clp": 132000
        },
        "budget_discount_clp": 0,
        "reimbursement": {
          "claimed_clp": 28000,
          "reimbursed_clp": 0,
          "pending_clp": 28000
        }
      }
    ],
    "totals": {
      "price_clp": 160000,
      "coverage_clp": 28000,
      "copay_clp": 132000
    },
    "reimbursement": {
      "request_count": 1,
      "requested_clp": 28000,
      "rejected_clp": 0,
      "reimbursed_clp": 0,
      "reimbursed_to_clinic_clp": 0,
      "reimbursed_to_patient_clp": 0,
      "already_counted_clp": 0,
      "outside_payments_clp": 0,
      "pending_clp": 28000
    }
  }
}

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

GET /clinic/patients/{id}/insurance-statement/render

Estado de cuenta with the seguro, as a PDF

The same figures as the JSON statement, on the clinic’s branded paper. Requires clinic_money: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.

ParameterInTypeRequiredConstraints
idpathuuidyes
insurance_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurance-statement/render \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /clinic/patients/{id}/insurances

List a patient’s seguros

Primary first. Besides, never instead of, the previsión on the ficha. Requires 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.

ParameterInTypeRequiredConstraints
idpathuuidyes
curl https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurances \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "items": [
      {
        "id": "c0e57121-0000-4000-8000-0000000007ad",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
        "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
        "insurer": "Consorcio",
        "plan_name": "Salud Plus",
        "plan_active": true,
        "policy_number": "P-48213",
        "holder_name": null,
        "valid_until": "2027-03-31",
        "is_primary": true,
        "created_at": "2026-10-09T17:45:00.000Z",
        "updated_at": "2026-10-09T17:45:00.000Z"
      }
    ],
    "total": 1
  }
}

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

POST /clinic/patients/{id}/insurances

Add a seguro to a patient

The patient’s first seguro is always the primary one; is_primary: true moves the flag from the current primary. 409 when the patient already holds that plan; 400 for a withdrawn plan. Works on any patient source: the seguro is Vitrina’s own record. Requires clinic:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes

Body

FieldTypeRequiredConstraints
plan_iduuidyes
policy_numberstring | null—máx. 60
holder_namestring | null—máx. 160
valid_untilstring | null—patrón ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
is_primaryboolean—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurances \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
    "policy_number": "P-48213",
    "valid_until": "2027-03-31"
  }'

Example response (201)

{
  "data": {
    "id": "c0e57121-0000-4000-8000-0000000007ad",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
    "insurer": "Consorcio",
    "plan_name": "Salud Plus",
    "plan_active": true,
    "policy_number": "P-48213",
    "holder_name": null,
    "valid_until": "2027-03-31",
    "is_primary": true,
    "created_at": "2026-10-09T17:45:00.000Z",
    "updated_at": "2026-10-09T17:45:00.000Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

DELETE /clinic/patients/{id}/insurances/{insuranceId}

Remove a patient’s seguro

Deleting the primary promotes the oldest remaining seguro. Requires clinic:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes
insuranceIdpathuuidyes
curl -X DELETE https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurances/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429

PATCH /clinic/patients/{id}/insurances/{insuranceId}

Edit a patient’s seguro

Any field. is_primary: true makes it the primary one; to demote the primary, mark another seguro primary (false is a 400). Requires clinic:write.

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.

ParameterInTypeRequiredConstraints
idpathuuidyes
insuranceIdpathuuidyes

Body

FieldTypeRequiredConstraints
plan_iduuid—
policy_numberstring | null—máx. 60
holder_namestring | null—máx. 160
valid_untilstring | null—patrón ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
is_primaryboolean—
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/patients/<id>/insurances/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "valid_until": "2027-06-30"
  }'

Example response (200)

{
  "data": {
    "id": "c0e57121-0000-4000-8000-0000000007ad",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "plan_id": "c0e57121-0000-4000-8000-0000000007ab",
    "insurer": "Consorcio",
    "plan_name": "Salud Plus",
    "plan_active": true,
    "policy_number": "P-48213",
    "holder_name": null,
    "valid_until": "2027-06-30",
    "is_primary": true,
    "created_at": "2026-10-09T17:45:00.000Z",
    "updated_at": "2026-10-09T17:45:00.000Z"
  }
}

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

On this page