VitrinaAPI

Plantillas y conservación

Los textos y las preguntas antes de que nadie los firme o los responda, y cuántos años se guarda cada tipo de documento. No nombran a ningún paciente, y por eso no son sensibles.

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

Descarga la proyección completa de la API pública: openapi.json.

Firmar y revocar un consentimiento explica este recurso en prosa, con ejemplos ejecutables.

MétodoRutaQué hace
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.

ParámetroEnTipoObligatorioRestricciones
statusquerydraft \published \retired
curl https://api.vitrinadev.com/api/v1/clinic/consent-templates \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
descriptionstring | nullmáx. 600
specialty_idstring | null
signature_methodstablet \public_link \whatsapp_otp \
body_htmlstringmá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
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
descriptionstring | nullmá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"
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
body_htmlstringmáx. 200000
allows_photosboolean
allows_marketing_useboolean
change_notestring | nullmá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"
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/retire \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consent-templates/<id>/versions \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
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"

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

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

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
descriptionstring | nullmá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"
                }
              ]
            }
          ]
        }
      ]
    }
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl https://api.vitrinadev.com/api/v1/clinic/form-templates/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 160
descriptionstring | nullmá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"
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
schemaobject
change_notestring | nullmá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?"
            }
          ]
        }
      ]
    }
  }'

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/publish \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/retire \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl -X POST https://api.vitrinadev.com/api/v1/clinic/form-templates/<id>/versions \
  -H "Authorization: Bearer $VITRINA_KEY"

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

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

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

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

Cuerpo

CampoTipoObligatorioRestricciones
kindexamen \imagen \informe \
retention_yearsinteger≥ 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
  }'

Ejemplo de respuesta (200)

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

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

En esta página