VitrinaAPI

Templates and retention

The texts and the questions before anybody signs or answers them, and how many years each kind of document is kept. They name no patient, and are therefore not sensitive.

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.

Sign and revoke consents explains this resource in prose, with runnable examples.

MethodPathWhat it does
GET/clinic/consent-templatesConsent templates
POST/clinic/consent-templatesCreate a consent template (with its first draft version)
GET/clinic/consent-templates/{id}One consent template with its versions, live and draft
PATCH/clinic/consent-templates/{id}Rename, re-scope or change the allowed signature methods
POST/clinic/consent-templates/{id}/draftSave the draft text and its clauses
POST/clinic/consent-templates/{id}/publishPublish the draft version
POST/clinic/consent-templates/{id}/retireRetire a consent template
POST/clinic/consent-templates/{id}/versionsStart version n+1 from the live one
GET/clinic/form-templatesForm templates (the configurable ficha)
POST/clinic/form-templatesCreate a form template (with its first draft version)
GET/clinic/form-templates/{id}One template with every version, the live one and the draft
PATCH/clinic/form-templates/{id}Rename or re-scope a template
POST/clinic/form-templates/{id}/draftSave the questions of the draft version
POST/clinic/form-templates/{id}/publishPublish the draft version
POST/clinic/form-templates/{id}/retireRetire a template
POST/clinic/form-templates/{id}/versionsStart version n+1 from the live one
GET/clinic/retentionRetention policy per document kind
PUT/clinic/retentionSet how many years one kind of document is kept

Consent templates

Requires clinic:read. Templates name no patient, so they write no PHI access event.

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
statusquerydraft \published \retired
curl https://api.vitrinadev.com/api/v1/clinic/consent-templates \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "data": [
      {
        "id": "3226ecbe-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "name": "Consentimiento de ortodoncia",
        "description": "Tratamiento de ortodoncia fija, riesgos y alternativas",
        "specialty_id": null,
        "status": "draft",
        "signature_methods": [
          "tablet",
          "public_link"
        ],
        "current_version": null,
        "published_at": null,
        "retired_at": null,
        "created_by_user_id": null,
        "created_at": "2026-09-22T22:21:17.641Z",
        "updated_at": "2026-09-22T22:21:17.641Z"
      }
    ],
    "total": 1
  }
}

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

POST /clinic/consent-templates

Create a consent template (with its first draft version)

signature_methods is per template: a consent a clinic insists on taking in the box must not be signable from a link. 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
namestringyesmín. 1, máx. 160
descriptionstring | null—máx. 600
specialty_idstring | null—
signature_methodstablet \public_link \whatsapp_otp \
body_htmlstring—máx. 200000
allows_photosboolean—
allows_marketing_useboolean—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Consentimiento de ortodoncia",
    "description": "Tratamiento de ortodoncia fija, riesgos y alternativas",
    "signature_methods": [
      "tablet",
      "public_link"
    ],
    "body_html": "<p>Declaro haber sido informada del tratamiento propuesto…</p>",
    "allows_photos": true
  }'

Example response (201)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Consentimiento de ortodoncia",
      "description": "Tratamiento de ortodoncia fija, riesgos y alternativas",
      "specialty_id": null,
      "status": "draft",
      "signature_methods": [
        "tablet",
        "public_link"
      ],
      "current_version": null,
      "published_at": null,
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.641Z"
    },
    "version": {
      "id": "c692273d-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000001",
      "version": 1,
      "body_html": "<p>Declaro haber sido informada del tratamiento propuesto…</p>",
      "allows_photos": true,
      "allows_marketing_use": false,
      "status": "draft",
      "change_note": null,
      "published_at": null,
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.641Z"
    }
  }
}

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

One consent template with its versions, live and draft

live is what a new consent would pin; allows_photos / allows_marketing_use say what the TEXT covers (the patient’s own answers live on the signed consent). 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/consent-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Consentimiento de ortodoncia",
      "description": "Tratamiento de ortodoncia fija, riesgos y alternativas",
      "specialty_id": null,
      "status": "draft",
      "signature_methods": [
        "tablet",
        "public_link"
      ],
      "current_version": null,
      "published_at": null,
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.641Z"
    },
    "versions": [
      {
        "id": "c692273d-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "template_id": "3226ecbe-0000-4000-8000-000000000001",
        "version": 1,
        "body_html": "<p>Declaro haber sido informada del tratamiento propuesto…</p>",
        "allows_photos": true,
        "allows_marketing_use": false,
        "status": "draft",
        "change_note": null,
        "published_at": null,
        "published_by_user_id": null,
        "created_by_user_id": null,
        "created_at": "2026-09-22T22:21:17.641Z",
        "updated_at": "2026-09-22T22:21:17.641Z"
      }
    ],
    "live": null,
    "draft": {
      "id": "c692273d-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000001",
      "version": 1,
      "body_html": "<p>Declaro haber sido informada del tratamiento propuesto…</p>",
      "allows_photos": true,
      "allows_marketing_use": false,
      "status": "draft",
      "change_note": null,
      "published_at": null,
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.641Z"
    }
  }
}

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

Rename, re-scope or change the allowed signature methods

Identity only — never the text. 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
namestring—mín. 1, máx. 160
descriptionstring | null—máx. 600
specialty_idstring | null—
signature_methodstablet \public_link \whatsapp_otp \
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Ortodoncia fija: riesgos, alternativas y cuidados"
  }'

Example response (200)

{
  "data": {
    "id": "3226ecbe-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Consentimiento de ortodoncia",
    "description": "Ortodoncia fija: riesgos, alternativas y cuidados",
    "specialty_id": null,
    "status": "draft",
    "signature_methods": [
      "tablet",
      "public_link"
    ],
    "current_version": null,
    "published_at": null,
    "retired_at": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.641Z",
    "updated_at": "2026-09-22T22:21:17.660Z"
  }
}

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

POST /clinic/consent-templates/{id}/draft

Save the draft text and its clauses

A PUBLISHED consent text can never be edited — it is the evidence of what a patient agreed to, and editing it retroactively changes what they agreed to. allows_marketing_use cannot be set without allows_photos. 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
body_htmlstring—máx. 200000
allows_photosboolean—
allows_marketing_useboolean—
change_notestring | null—máx. 400
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/draft \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "change_note": "Se agregan las alternativas"
  }'

Example response (200)

{
  "data": {
    "id": "c692273d-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "version": 1,
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false,
    "status": "draft",
    "change_note": "Se agregan las alternativas",
    "published_at": null,
    "published_by_user_id": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.641Z",
    "updated_at": "2026-09-22T22:21:17.668Z"
  }
}

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

POST /clinic/consent-templates/{id}/publish

Publish the draft version

Refused when the text is empty: the text is what the patient agrees to. 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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Consentimiento de ortodoncia",
      "description": "Ortodoncia fija: riesgos, alternativas y cuidados",
      "specialty_id": null,
      "status": "published",
      "signature_methods": [
        "tablet",
        "public_link"
      ],
      "current_version": 1,
      "published_at": "2026-09-22T22:21:17.677Z",
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.675Z"
    },
    "version": {
      "id": "c692273d-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000001",
      "version": 1,
      "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
      "allows_photos": true,
      "allows_marketing_use": false,
      "status": "published",
      "change_note": "Se agregan las alternativas",
      "published_at": "2026-09-22T22:21:17.677Z",
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.641Z",
      "updated_at": "2026-09-22T22:21:17.675Z"
    }
  }
}

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

POST /clinic/consent-templates/{id}/retire

Retire a consent template

No new consents; every signed one keeps rendering its pinned text. 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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/retire \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "f5a82842-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Consentimiento en desuso",
    "description": null,
    "specialty_id": null,
    "status": "retired",
    "signature_methods": [
      "tablet",
      "public_link"
    ],
    "current_version": null,
    "published_at": null,
    "retired_at": "2026-09-22T22:21:17.772Z",
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.764Z",
    "updated_at": "2026-09-22T22:21:17.771Z"
  }
}

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

POST /clinic/consent-templates/{id}/versions

Start version n+1 from the live one

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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/versions \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "id": "c692273d-0000-4000-8000-000000000002",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "version": 2,
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false,
    "status": "draft",
    "change_note": null,
    "published_at": null,
    "published_by_user_id": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.687Z",
    "updated_at": "2026-09-22T22:21:17.687Z"
  }
}

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

GET /clinic/form-templates

Form templates (the configurable ficha)

The «tipo de ficha» catalogue. Requires clinic:read — a receptionist picking a ficha must be able to list them. Templates name no patient, so they write no PHI access event.

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
statusquerydraft \published \retired
kindqueryintake \encounter \followup
specialty_idqueryuuidno
patient_fillablequerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/form-templates \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "data": [
      {
        "id": "3226ecbe-0000-4000-8000-000000000002",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "name": "Ficha de ingreso",
        "description": null,
        "specialty_id": null,
        "kind": "intake",
        "status": "draft",
        "patient_fillable": true,
        "current_version": null,
        "published_at": null,
        "retired_at": null,
        "created_by_user_id": null,
        "created_at": "2026-09-22T22:21:17.699Z",
        "updated_at": "2026-09-22T22:21:17.699Z"
      }
    ],
    "total": 1
  }
}

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

POST /clinic/form-templates

Create a form template (with its first draft version)

A template is never born without a version — the row and its v1 draft are written in one transaction. 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
namestringyesmín. 1, máx. 160
descriptionstring | null—máx. 600
specialty_idstring | null—
kindintake \encounter \followup
patient_fillableboolean—
schemaobject—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ficha de ingreso",
    "kind": "intake",
    "patient_fillable": true,
    "schema": {
      "sections": [
        {
          "key": "salud",
          "title": "Antecedentes de salud",
          "fields": [
            {
              "key": "alergias",
              "type": "text",
              "label": "¿Tiene alergias?",
              "required": true
            },
            {
              "key": "embarazo",
              "type": "select",
              "label": "¿Está embarazada?",
              "options": [
                {
                  "value": "si",
                  "label": "Sí"
                },
                {
                  "value": "no",
                  "label": "No"
                }
              ]
            }
          ]
        }
      ]
    }
  }'

Example response (201)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000002",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Ficha de ingreso",
      "description": null,
      "specialty_id": null,
      "kind": "intake",
      "status": "draft",
      "patient_fillable": true,
      "current_version": null,
      "published_at": null,
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.699Z"
    },
    "version": {
      "id": "c692273d-0000-4000-8000-000000000003",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000002",
      "version": 1,
      "schema": {
        "sections": [
          {
            "key": "salud",
            "title": "Antecedentes de salud",
            "fields": [
              {
                "key": "alergias",
                "type": "text",
                "label": "¿Tiene alergias?",
                "required": true
              },
              {
                "key": "embarazo",
                "type": "select",
                "label": "¿Está embarazada?",
                "options": [
                  {
                    "label": "Sí",
                    "value": "si"
                  },
                  {
                    "label": "No",
                    "value": "no"
                  }
                ]
              }
            ]
          }
        ]
      },
      "status": "draft",
      "change_note": null,
      "published_at": null,
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.699Z"
    }
  }
}

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

GET /clinic/form-templates/{id}

One template with every version, the live one and the draft

The editor reads this: live is what a new response would pin, draft is what «publicar cambios» would promote. 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/form-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000002",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Ficha de ingreso",
      "description": null,
      "specialty_id": null,
      "kind": "intake",
      "status": "draft",
      "patient_fillable": true,
      "current_version": null,
      "published_at": null,
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.699Z"
    },
    "versions": [
      {
        "id": "c692273d-0000-4000-8000-000000000003",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "template_id": "3226ecbe-0000-4000-8000-000000000002",
        "version": 1,
        "schema": {
          "sections": [
            {
              "key": "salud",
              "title": "Antecedentes de salud",
              "fields": [
                {
                  "key": "alergias",
                  "type": "text",
                  "label": "¿Tiene alergias?",
                  "required": true
                },
                {
                  "key": "embarazo",
                  "type": "select",
                  "label": "¿Está embarazada?",
                  "options": [
                    {
                      "label": "Sí",
                      "value": "si"
                    },
                    {
                      "label": "No",
                      "value": "no"
                    }
                  ]
                }
              ]
            }
          ]
        },
        "status": "draft",
        "change_note": null,
        "published_at": null,
        "published_by_user_id": null,
        "created_by_user_id": null,
        "created_at": "2026-09-22T22:21:17.699Z",
        "updated_at": "2026-09-22T22:21:17.699Z"
      }
    ],
    "live": null,
    "draft": {
      "id": "c692273d-0000-4000-8000-000000000003",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000002",
      "version": 1,
      "schema": {
        "sections": [
          {
            "key": "salud",
            "title": "Antecedentes de salud",
            "fields": [
              {
                "key": "alergias",
                "type": "text",
                "label": "¿Tiene alergias?",
                "required": true
              },
              {
                "key": "embarazo",
                "type": "select",
                "label": "¿Está embarazada?",
                "options": [
                  {
                    "label": "Sí",
                    "value": "si"
                  },
                  {
                    "label": "No",
                    "value": "no"
                  }
                ]
              }
            ]
          }
        ]
      },
      "status": "draft",
      "change_note": null,
      "published_at": null,
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.699Z"
    }
  }
}

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

PATCH /clinic/form-templates/{id}

Rename or re-scope a template

IDENTITY ONLY — never the questions. Renaming a ficha must not fork it, which is exactly why the questions live on a version and not here. 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
namestring—mín. 1, máx. 160
descriptionstring | null—máx. 600
specialty_idstring | null—
kindintake \encounter \followup
patient_fillableboolean—
curl -X PATCH https://api.vitrinadev.com/api/v1/clinic/form-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "La completa el paciente antes de la primera atención"
  }'

Example response (200)

{
  "data": {
    "id": "3226ecbe-0000-4000-8000-000000000002",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Ficha de ingreso",
    "description": "La completa el paciente antes de la primera atención",
    "specialty_id": null,
    "kind": "intake",
    "status": "draft",
    "patient_fillable": true,
    "current_version": null,
    "published_at": null,
    "retired_at": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.699Z",
    "updated_at": "2026-09-22T22:21:17.730Z"
  }
}

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

POST /clinic/form-templates/{id}/draft

Save the questions of the draft version

Creates the draft if there is none. A PUBLISHED version can never be edited — the database refuses it — because it is what existing responses render against. 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
schemaobjectyes
change_notestring | null—máx. 400
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/draft \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "change_note": "Se agrega la pregunta por medicamentos",
    "schema": {
      "sections": [
        {
          "key": "salud",
          "title": "Antecedentes de salud",
          "fields": [
            {
              "key": "alergias",
              "type": "text",
              "label": "¿Tiene alergias?",
              "required": true
            },
            {
              "key": "medicamentos",
              "type": "text",
              "label": "¿Toma algún medicamento?"
            }
          ]
        }
      ]
    }
  }'

Example response (200)

{
  "data": {
    "id": "c692273d-0000-4000-8000-000000000003",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000002",
    "version": 1,
    "schema": {
      "sections": [
        {
          "key": "salud",
          "title": "Antecedentes de salud",
          "fields": [
            {
              "key": "alergias",
              "type": "text",
              "label": "¿Tiene alergias?",
              "required": true
            },
            {
              "key": "medicamentos",
              "type": "text",
              "label": "¿Toma algún medicamento?"
            }
          ]
        }
      ]
    },
    "status": "draft",
    "change_note": "Se agrega la pregunta por medicamentos",
    "published_at": null,
    "published_by_user_id": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.699Z",
    "updated_at": "2026-09-22T22:21:17.739Z"
  }
}

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

POST /clinic/form-templates/{id}/publish

Publish the draft version

The draft becomes live, the previous live version is retired, and the template points at the new number — one transaction. Existing responses keep rendering the version they pinned. 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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "template": {
      "id": "3226ecbe-0000-4000-8000-000000000002",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Ficha de ingreso",
      "description": "La completa el paciente antes de la primera atención",
      "specialty_id": null,
      "kind": "intake",
      "status": "published",
      "patient_fillable": true,
      "current_version": 1,
      "published_at": "2026-09-22T22:21:17.748Z",
      "retired_at": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.746Z"
    },
    "version": {
      "id": "c692273d-0000-4000-8000-000000000003",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "template_id": "3226ecbe-0000-4000-8000-000000000002",
      "version": 1,
      "schema": {
        "sections": [
          {
            "key": "salud",
            "title": "Antecedentes de salud",
            "fields": [
              {
                "key": "alergias",
                "type": "text",
                "label": "¿Tiene alergias?",
                "required": true
              },
              {
                "key": "medicamentos",
                "type": "text",
                "label": "¿Toma algún medicamento?"
              }
            ]
          }
        ]
      },
      "status": "published",
      "change_note": "Se agrega la pregunta por medicamentos",
      "published_at": "2026-09-22T22:21:17.748Z",
      "published_by_user_id": null,
      "created_by_user_id": null,
      "created_at": "2026-09-22T22:21:17.699Z",
      "updated_at": "2026-09-22T22:21:17.746Z"
    }
  }
}

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

POST /clinic/form-templates/{id}/retire

Retire a template

No new responses; every existing one still renders forever. 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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/retire \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "f5a82842-0000-4000-8000-000000000002",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Ficha en desuso",
    "description": null,
    "specialty_id": null,
    "kind": "intake",
    "status": "retired",
    "patient_fillable": false,
    "current_version": null,
    "published_at": null,
    "retired_at": "2026-09-22T22:21:17.785Z",
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.776Z",
    "updated_at": "2026-09-22T22:21:17.784Z"
  }
}

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

POST /clinic/form-templates/{id}/versions

Start version n+1 from the live one

Seeded from the published version, because a new version of a ficha is almost always the old one plus a question. Idempotent: a template that already has a draft returns it. 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
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/versions \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (201)

{
  "data": {
    "id": "c692273d-0000-4000-8000-000000000004",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000002",
    "version": 2,
    "schema": {
      "sections": [
        {
          "key": "salud",
          "title": "Antecedentes de salud",
          "fields": [
            {
              "key": "alergias",
              "type": "text",
              "label": "¿Tiene alergias?",
              "required": true
            },
            {
              "key": "medicamentos",
              "type": "text",
              "label": "¿Toma algún medicamento?"
            }
          ]
        }
      ]
    },
    "status": "draft",
    "change_note": null,
    "published_at": null,
    "published_by_user_id": null,
    "created_by_user_id": null,
    "created_at": "2026-09-22T22:21:17.756Z",
    "updated_at": "2026-09-22T22:21:17.756Z"
  }
}

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

GET /clinic/retention

Retention policy per document kind

EVERY kind is returned, always — a settings screen with holes in it is a screen a clinic cannot reason about. Kinds the tenant never configured render their legal floor and say so (is_default). 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.

curl https://api.vitrinadev.com/api/v1/clinic/retention \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "data": [
      {
        "kind": "examen",
        "retention_years": 15,
        "legal_minimum_years": 15,
        "is_default": true
      },
      {
        "kind": "imagen",
        "retention_years": 15,
        "legal_minimum_years": 15,
        "is_default": true
      },
      {
        "kind": "informe",
        "retention_years": 15,
        "legal_minimum_years": 15,
        "is_default": true
      }
    ],
    "total": 10
  }
}

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

PUT /clinic/retention

Set how many years one kind of document is kept

REFUSED below the legal minimum, with the legal reason — 15 years for clinical documents (Ley 20.584 / Decreto 41), 6 for tax documents (SII). The floor is also a CHECK constraint, so it is a property of the database rather than of a form validator. 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
kindexamen \imagen \informe \
retention_yearsintegeryes≥ 1, ≤ 100
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/retention \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "consentimiento",
    "retention_years": 15
  }'

Example response (200)

{
  "data": {
    "kind": "consentimiento",
    "retention_years": 15,
    "legal_minimum_years": 15,
    "is_default": false
  }
}

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

On this page