VitrinaAPI

Consentimientos

El consentimiento informado contra el texto exacto que se firmó, su revocación y la galería clínica que ese consentimiento habilita. Marcado como sensible.

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/consents«Consentimientos» — firmados, pendientes y revocados
GET/clinic/consents/{id}One consent with the text it was signed against
POST/clinic/consents/{id}/linkMint the tokenised signing link
POST/clinic/consents/{id}/revokeRevoke a signed consent (with a reason)
POST/clinic/consents/{id}/signSign a consent (staff-assisted)
GET/clinic/patients/{id}/consentsConsents of one patient, newest first
POST/clinic/patients/{id}/consentsRequest a consent (pending, pinned to the live version)
GET/clinic/patients/{id}/photosGalería — before/after images by zona and fecha
POST/clinic/patients/{id}/photosUpload a clinical image (CONSENT-GATED)
POST/clinic/photos/{id}/deleteDelete a clinical image (tombstone, with a reason)

GET /clinic/consents

«Consentimientos» — firmados, pendientes y revocados

Across patients, filterable by template and status. A WORKLIST: each row carries a name and a status, never the consent TEXT or a signature. Requires clinic_record:read; opening one row is the disclosure that writes an access event.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
clinic_patient_idqueryuuidno
template_idqueryuuidno
statusquerypending \signed \revoked \
pagequeryintegerno≥ 1, por defecto 1
limitqueryintegerno≥ 1, ≤ 200, por defecto 50
curl https://api.vitrinadev.com/api/v1/clinic/consents \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "data": [
      {
        "id": "15b61788-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "template_id": "3226ecbe-0000-4000-8000-000000000001",
        "template_version_id": "c692273d-0000-4000-8000-000000000001",
        "template_version": 1,
        "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
        "encounter_id": "434a1a10-0000-4000-8000-000000000001",
        "appointment_id": null,
        "status": "pending",
        "signed_by_contact_id": null,
        "signed_by_name": null,
        "signed_by_document": null,
        "signed_by_role": null,
        "signature_method": null,
        "signature_storage_key": null,
        "signature_mime_type": null,
        "signed_at": null,
        "signed_ip": null,
        "signed_user_agent": null,
        "signed_by_user_id": null,
        "photo_consent": null,
        "marketing_use_consent": null,
        "rendered_document_id": null,
        "revoked_at": null,
        "revoked_reason": null,
        "revoked_by_user_id": null,
        "expires_at": null,
        "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
        "created_at": "2026-09-22T22:21:17.807Z",
        "updated_at": "2026-09-22T22:21:17.807Z",
        "template_name": "Consentimiento de ortodoncia",
        "patient_name": "María José Fuentes Lagos"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

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

GET /clinic/consents/{id}

One consent with the text it was signed against

Requires clinic_record:read AND the PHI gate. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

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

Ejemplo de respuesta (200)

{
  "data": {
    "id": "15b61788-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "template_version_id": "c692273d-0000-4000-8000-000000000001",
    "template_version": 1,
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "encounter_id": "434a1a10-0000-4000-8000-000000000001",
    "appointment_id": null,
    "status": "pending",
    "signed_by_contact_id": null,
    "signed_by_name": null,
    "signed_by_document": null,
    "signed_by_role": null,
    "signature_method": null,
    "signature_storage_key": null,
    "signature_mime_type": null,
    "signed_at": null,
    "signed_ip": null,
    "signed_user_agent": null,
    "signed_by_user_id": null,
    "photo_consent": null,
    "marketing_use_consent": null,
    "rendered_document_id": null,
    "revoked_at": null,
    "revoked_reason": null,
    "revoked_by_user_id": null,
    "expires_at": null,
    "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
    "created_at": "2026-09-22T22:21:17.807Z",
    "updated_at": "2026-09-22T22:21:17.807Z",
    "template_name": "Consentimiento de ortodoncia",
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false
  }
}

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

POST /clinic/consents/{id}/link

Mint the tokenised signing link

Refused unless the template allows public_link. The token is HS256 and unrevocable, so the ROW’s own status — re-read on every request — is what decides whether it is still signable. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

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

Ejemplo de respuesta (200)

{
  "data": {
    "consent_id": "15b61788-0000-4000-8000-000000000001",
    "url": "http://localhost:3000/consentimiento/<token>",
    "expires_in_seconds": 1209600
  }
}

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

POST /clinic/consents/{id}/revoke

Revoke a signed consent (with a reason)

Never a delete: a status, a date and a reason. Revoking a consent with the photo clause immediately closes the gallery gate for that patient. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
reasonstringmín. 3, máx. 600
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consents/<id>/revoke \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "La paciente retira su autorización para fotografías"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "e85a0f49-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "template_version_id": "c692273d-0000-4000-8000-000000000001",
    "template_version": 1,
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "encounter_id": null,
    "appointment_id": null,
    "status": "revoked",
    "signed_by_contact_id": null,
    "signed_by_name": "María José Fuentes Lagos",
    "signed_by_document": null,
    "signed_by_role": null,
    "signature_method": "public_link",
    "signature_storage_key": null,
    "signature_mime_type": null,
    "signed_at": "2026-09-22T22:21:17.876Z",
    "signed_ip": "::ffff:127.0.0.1",
    "signed_user_agent": "node",
    "signed_by_user_id": "11111111-0000-4000-8000-000000000001",
    "photo_consent": true,
    "marketing_use_consent": null,
    "rendered_document_id": null,
    "revoked_at": "2026-09-22T22:21:17.886Z",
    "revoked_reason": "La paciente retira su autorización para fotografías",
    "revoked_by_user_id": "11111111-0000-4000-8000-000000000001",
    "expires_at": null,
    "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
    "created_at": "2026-09-22T22:21:17.864Z",
    "updated_at": "2026-09-22T22:21:17.885Z",
    "template_name": "Consentimiento de ortodoncia",
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false
  }
}

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

POST /clinic/consents/{id}/sign

Sign a consent (staff-assisted)

Records the signer’s name, document and role, the method, the signature image, the instant, the IP and the user agent. MINORS: a patient under 18 cannot sign for themself — the signer must be an apoderado or a tutor. photo_consent is TRI-STATE and must not be defaulted: null means the question was never asked. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
signed_by_namestringmín. 2, máx. 200
signed_by_documentstring | nullmáx. 40
signed_by_roletitular \apoderado \tutor \
signed_by_contact_idstring | null
signature_methodtablet \public_link \whatsapp_otp \
signature_imagestring | nullmín. 16, máx. 1400000
photo_consentboolean | null
marketing_use_consentboolean | null
curl -X POST https://api.vitrinadev.com/api/v1/clinic/consents/<id>/sign \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signed_by_name": "María José Fuentes Lagos",
    "signed_by_document": "11.111.111-1",
    "signed_by_role": "titular",
    "signed_by_contact_id": "1a73af9e-0000-4000-8000-000000000001",
    "signature_method": "tablet",
    "photo_consent": true,
    "marketing_use_consent": false
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "15b61788-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "template_version_id": "c692273d-0000-4000-8000-000000000001",
    "template_version": 1,
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "encounter_id": "434a1a10-0000-4000-8000-000000000001",
    "appointment_id": null,
    "status": "signed",
    "signed_by_contact_id": "1a73af9e-0000-4000-8000-000000000001",
    "signed_by_name": "María José Fuentes Lagos",
    "signed_by_document": "11.111.111-1",
    "signed_by_role": "titular",
    "signature_method": "tablet",
    "signature_storage_key": null,
    "signature_mime_type": null,
    "signed_at": "2026-09-22T22:21:17.852Z",
    "signed_ip": "::ffff:127.0.0.1",
    "signed_user_agent": "node",
    "signed_by_user_id": "11111111-0000-4000-8000-000000000001",
    "photo_consent": true,
    "marketing_use_consent": false,
    "rendered_document_id": null,
    "revoked_at": null,
    "revoked_reason": null,
    "revoked_by_user_id": null,
    "expires_at": null,
    "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
    "created_at": "2026-09-22T22:21:17.807Z",
    "updated_at": "2026-09-22T22:21:17.851Z",
    "template_name": "Consentimiento de ortodoncia",
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false
  }
}

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

GET /clinic/patients/{id}/consents

Consents of one patient, newest first

Each one carries the TEXT it pinned and the patient’s tri-state answers. Requires clinic_record:read AND the per-patient PHI gate. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

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

Ejemplo de respuesta (200)

{
  "data": {
    "data": [
      {
        "id": "15b61788-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "template_id": "3226ecbe-0000-4000-8000-000000000001",
        "template_version_id": "c692273d-0000-4000-8000-000000000001",
        "template_version": 1,
        "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
        "encounter_id": "434a1a10-0000-4000-8000-000000000001",
        "appointment_id": null,
        "status": "pending",
        "signed_by_contact_id": null,
        "signed_by_name": null,
        "signed_by_document": null,
        "signed_by_role": null,
        "signature_method": null,
        "signature_storage_key": null,
        "signature_mime_type": null,
        "signed_at": null,
        "signed_ip": null,
        "signed_user_agent": null,
        "signed_by_user_id": null,
        "photo_consent": null,
        "marketing_use_consent": null,
        "rendered_document_id": null,
        "revoked_at": null,
        "revoked_reason": null,
        "revoked_by_user_id": null,
        "expires_at": null,
        "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
        "created_at": "2026-09-22T22:21:17.807Z",
        "updated_at": "2026-09-22T22:21:17.807Z",
        "template_name": "Consentimiento de ortodoncia",
        "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
        "allows_photos": true,
        "allows_marketing_use": false
      }
    ],
    "total": 1
  }
}

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

POST /clinic/patients/{id}/consents

Request a consent (pending, pinned to the live version)

Mints the row a tablet or a link then signs. The version is resolved once, here. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
template_iduuid
encounter_idstring | null
appointment_idstring | null
expires_atstring | nulldate-time
curl -X POST https://api.vitrinadev.com/api/v1/clinic/patients/<id>/consents \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "encounter_id": "434a1a10-0000-4000-8000-000000000001"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "15b61788-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "template_id": "3226ecbe-0000-4000-8000-000000000001",
    "template_version_id": "c692273d-0000-4000-8000-000000000001",
    "template_version": 1,
    "clinic_patient_id": "b1b0b8de-0000-4000-8000-000000000001",
    "encounter_id": "434a1a10-0000-4000-8000-000000000001",
    "appointment_id": null,
    "status": "pending",
    "signed_by_contact_id": null,
    "signed_by_name": null,
    "signed_by_document": null,
    "signed_by_role": null,
    "signature_method": null,
    "signature_storage_key": null,
    "signature_mime_type": null,
    "signed_at": null,
    "signed_ip": null,
    "signed_user_agent": null,
    "signed_by_user_id": null,
    "photo_consent": null,
    "marketing_use_consent": null,
    "rendered_document_id": null,
    "revoked_at": null,
    "revoked_reason": null,
    "revoked_by_user_id": null,
    "expires_at": null,
    "requested_by_user_id": "11111111-0000-4000-8000-000000000001",
    "created_at": "2026-09-22T22:21:17.807Z",
    "updated_at": "2026-09-22T22:21:17.807Z",
    "template_name": "Consentimiento de ortodoncia",
    "body_html": "<p>Declaro haber sido informada del tratamiento, sus riesgos y sus alternativas.</p>",
    "allows_photos": true,
    "allows_marketing_use": false
  }
}

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

GET /clinic/patients/{id}/photos

Galería — before/after images by zona and fecha

Rows sharing a pair_id are one side-by-side comparison. Bytes are returned INLINE (with_bytes=true) rather than as a signed URL: the bucket is private and a clinical photograph is the last object that should acquire a forwardable link. photo_consent in the envelope says whether new images may currently be added. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid
zonequerystringnomáx. 120
with_bytesquerytrue \falseno
curl https://api.vitrinadev.com/api/v1/clinic/patients/<id>/photos \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "data": [],
    "total": 0,
    "photo_consent": true
  }
}

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

POST /clinic/patients/{id}/photos

Upload a clinical image (CONSENT-GATED)

REFUSED unless a live signed consent exists whose pinned text carries the photo clause AND whose photo_consent answer is TRUE — "never asked" is not a yes. The authorising consent id is written onto the row (consent_id is NOT NULL), so the permission is recorded and not merely checked. The gate runs BEFORE a single byte reaches the bucket. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
zonestringmín. 1, máx. 120
phaseantes \durante \despues \
captured_onstringpatrón ^\d{4}-\d{2}-\d{2}$
pair_idstring | null
captionstring | nullmáx. 400
encounter_idstring | null
imagestringmín. 64, máx. 17000000
curl -X POST https://api.vitrinadev.com/api/v1/clinic/patients/<id>/photos \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "zone": "frontal",
    "phase": "antes",
    "captured_on": "2026-09-24",
    "caption": "Frontal en máxima intercuspidación",
    "encounter_id": "14141414-0000-4000-8000-000000000001",
    "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "15151515-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "clinic_patient_id": "12121212-0000-4000-8000-000000000001",
    "encounter_id": "14141414-0000-4000-8000-000000000001",
    "consent_id": null,
    "zone": "frontal",
    "phase": "antes",
    "captured_on": "2026-09-24",
    "pair_id": null,
    "mime_type": "image/png",
    "size_bytes": 95,
    "caption": "Frontal en máxima intercuspidación",
    "uploaded_by_user_id": "11111111-0000-4000-8000-000000000001",
    "deleted_at": null,
    "deleted_reason": null,
    "created_at": "2026-09-24T13:41:02.118Z",
    "updated_at": "2026-09-24T13:41:02.118Z",
    "data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
  }
}

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

POST /clinic/photos/{id}/delete

Delete a clinical image (tombstone, with a reason)

The bytes go; the row stays with who uploaded it, under which consent and when. A POST rather than a DELETE because the reason is required. Every call writes a clinic_record_access_log row; a call that cannot write its access event fails.

Connected apps: refused with 403 CONNECTED_APP_SENSITIVE_DATA, whatever scopes they hold — this operation carries a dato sensible (ADR 0106 §4). Only an API key or a personal token of the workspace that holds the scope reaches it.

ParámetroEnTipoObligatorioRestricciones
idpathuuid

Cuerpo

CampoTipoObligatorioRestricciones
reasonstringmín. 3, máx. 400
curl -X POST https://api.vitrinadev.com/api/v1/clinic/photos/<id>/delete \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Foto desenfocada, se repite"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "15151515-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "clinic_patient_id": "12121212-0000-4000-8000-000000000001",
    "encounter_id": "14141414-0000-4000-8000-000000000001",
    "consent_id": null,
    "zone": "frontal",
    "phase": "antes",
    "captured_on": "2026-09-24",
    "pair_id": null,
    "mime_type": "image/png",
    "size_bytes": 95,
    "caption": "Frontal en máxima intercuspidación",
    "uploaded_by_user_id": "11111111-0000-4000-8000-000000000001",
    "deleted_at": "2026-09-24T18:02:55.401Z",
    "deleted_reason": "Foto desenfocada, se repite",
    "created_at": "2026-09-24T13:41:02.118Z",
    "updated_at": "2026-09-24T13:41:02.118Z",
    "data_url": null
  }
}

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

En esta página