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.
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étodo | Ruta | Qué 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}/link | Mint the tokenised signing link |
POST | /clinic/consents/{id}/revoke | Revoke a signed consent (with a reason) |
POST | /clinic/consents/{id}/sign | Sign a consent (staff-assisted) |
GET | /clinic/patients/{id}/consents | Consents of one patient, newest first |
POST | /clinic/patients/{id}/consents | Request a consent (pending, pinned to the live version) |
GET | /clinic/patients/{id}/photos | Galería — before/after images by zona and fecha |
POST | /clinic/patients/{id}/photos | Upload a clinical image (CONSENT-GATED) |
POST | /clinic/photos/{id}/delete | Delete 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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
clinic_patient_id | query | uuid | no | |
template_id | query | uuid | no | |
status | query | pending \ | signed \ | revoked \ |
page | query | integer | no | ≥ 1, por defecto 1 |
limit | query | integer | no | ≥ 1, ≤ 200, por defecto 50 |
curl https://api.vitrinadev.com/api/v1/clinic/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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
reason | string | sí | mí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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
signed_by_name | string | sí | mín. 2, máx. 200 |
signed_by_document | string | null | — | máx. 40 |
signed_by_role | titular \ | apoderado \ | tutor \ |
signed_by_contact_id | string | null | — | |
signature_method | tablet \ | public_link \ | whatsapp_otp \ |
signature_image | string | null | — | mín. 16, máx. 1400000 |
photo_consent | boolean | null | — | |
marketing_use_consent | boolean | 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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
template_id | uuid | sí | |
encounter_id | string | null | — | |
appointment_id | string | null | — | |
expires_at | string | null | — | date-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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí | |
zone | query | string | no | máx. 120 |
with_bytes | query | true \ | false | no |
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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
zone | string | sí | mín. 1, máx. 120 |
phase | antes \ | durante \ | despues \ |
captured_on | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
pair_id | string | null | — | |
caption | string | null | — | máx. 400 |
encounter_id | string | null | — | |
image | string | sí | mí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ámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
reason | string | sí | mí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