VitrinaAPI

Recursos humanos de la clínica

La conexión de solo lectura con Buk y el costo de nómina del mes, sumado a partir de las liquidaciones cerradas sin guardar el detalle por persona.

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.

MétodoRutaQué hace
PUT/clinic/hr/buk/conceptChoose the Buk concept for commissions
POST/clinic/hr/buk/connectConnect (or rotate) the tenant’s Buk API token
POST/clinic/hr/buk/disconnectDisconnect and wipe the stored Buk token
GET/clinic/hr/buk/itemsList the Buk concepts the clinic can pick
GET/clinic/hr/buk/mappingBuk mapping: professionals ↔ Buk employees and the commission concept
DELETE/clinic/hr/buk/mapping/{professionalId}Remove a professional’s Buk match
POST/clinic/hr/buk/mapping/confirmConfirm suggested Buk matches
POST/clinic/hr/buk/mapping/matchFind the professionals in Buk by RUT
POST/clinic/hr/buk/pushEnviar a Buk — push a closed commission period as variable pay
GET/clinic/hr/buk/pushesBuk push log
GET/clinic/hr/buk/statusBuk payroll connection status
POST/clinic/hr/buk/syncSincronizar ahora — pull last month’s payroll cost from Buk

PUT /clinic/hr/buk/concept

Choose the Buk concept for commissions

Maps the commission liquidación to one Buk item. Refused (422) unless the id is in GET /clinic/hr/buk/items.

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
buk_item_idintegersí> 0
curl -X PUT https://api.vitrinadev.com/api/v1/clinic/hr/buk/concept \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "buk_item_id": 4021
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "buk_item_id": 4021,
    "buk_item_name": "Bono por comisiones"
  }
}

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

POST /clinic/hr/buk/connect

Connect (or rotate) the tenant’s Buk API token

One Buk (buk.cl) connection per tenant: the company subdomain plus a read-only auth_token API key a human mints inside Buk (Configuración → Accesos API). Every sync reads ONLY the payroll_detail/month operation (Buk has no company-wide aggregate endpoint) and sums the CLOSED settlements in memory — the raw per-employee response (which carries rut) never leaves the provider client. Upserts BY TENANT — a second call with a new token rotates the stored credential rather than creating a second connection. The token is AES-GCM enveloped under the tenant’s own DEK before it is written; only a masked preview is ever returned.

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
subdomainstringsímín. 2, máx. 63, patrón ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
auth_tokenstringsímín. 8, máx. 500
countrychile—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/connect \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "clinicaodm",
    "auth_token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "country": "chile"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "connected": true,
    "id": "b6b6b6b6-0000-4000-8000-000000000001",
    "subdomain": "clinicaodm",
    "country": "chile",
    "has_credentials": true,
    "auth_token_preview": "a1b2••••d6",
    "enabled": true,
    "last_sync_at": "2026-09-22T09:05:11.000Z",
    "last_sync_status": "ok",
    "last_sync_error": null,
    "created_at": "2026-08-01T13:00:00.000Z",
    "updated_at": "2026-09-22T09:05:11.000Z"
  }
}

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

POST /clinic/hr/buk/disconnect

Disconnect and wipe the stored Buk token

Disables the connection and erases the stored token — never leaves an encrypted secret behind with no way to use it. Reconnecting is POST .../connect again with a fresh token.

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 -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/disconnect \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connected": false,
    "disconnected": true
  }
}

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

GET /clinic/hr/buk/items

List the Buk concepts the clinic can pick

Buk's variable-items API, narrowed to assignable haberes with a per-person variable amount.

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/hr/buk/items \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "id": 4021,
        "name": "Bono por comisiones",
        "code": "BONO_COM"
      }
    ]
  }
}

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

GET /clinic/hr/buk/mapping

Buk mapping: professionals ↔ Buk employees and the commission concept

Buk stays the payroll system of record; Vitrina computes and exports, and never pays. The key Buk issued needs ítems «Lectura y Modificación», empleados «Lectura» and, to attach the liquidación PDF, «subir documentos». Each professional carries its match state and the plain next step; ready_to_push says whether an export can run.

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/hr/buk/mapping \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connected": true,
    "concept": {
      "buk_item_id": 4021,
      "buk_item_name": "Bono por comisiones"
    },
    "professionals": [
      {
        "professional_id": "b6b6b6b6-0000-4000-8000-000000000011",
        "name": "Camila Rojas Soto",
        "rut": "12.345.678-5",
        "state": "confirmed",
        "buk_employee_name": "Camila Rojas Soto",
        "confirmed_at": "2026-10-01T14:02:00.000Z",
        "next_action": "Listo para enviar."
      }
    ],
    "counts": {
      "confirmed": 1,
      "suggested": 0,
      "needs_attention": 0
    },
    "ready_to_push": true
  }
}

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

DELETE /clinic/hr/buk/mapping/{professionalId}

Remove a professional’s Buk match

The professional is blocked from pushes until matched again. Items already in Buk are untouched.

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 -X DELETE https://api.vitrinadev.com/api/v1/clinic/hr/buk/mapping/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "removed": true
  }
}

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

POST /clinic/hr/buk/mapping/confirm

Confirm suggested Buk matches

The owner confirms once; only confirmed matches are pushed to. Omit professional_ids to confirm every suggestion.

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
professional_idsuuid[]—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/mapping/confirm \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "professional_ids": [
      "b6b6b6b6-0000-4000-8000-000000000011"
    ]
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "confirmed": 1
  }
}

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

POST /clinic/hr/buk/mapping/match

Find the professionals in Buk by RUT

Reads Buk’s employee list in memory (RUTs are never stored) and records each RUT hit as a SUGGESTION. A confirmed match is never touched. Professionals without a match come back with a reason.

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 -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/mapping/match \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "matched": 4,
    "unmatched": [
      {
        "professional_id": "b6b6b6b6-0000-4000-8000-000000000012",
        "name": "Daniel Pino Vega",
        "reason_code": "not_in_buk",
        "reason": "No hay un colaborador con este RUT en Buk."
      }
    ]
  }
}

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

POST /clinic/hr/buk/push

Enviar a Buk — push a closed commission period as variable pay

Buk stays the payroll system of record; Vitrina computes and exports, and never pays. The key Buk issued needs ítems «Lectura y Modificación», empleados «Lectura» and, to attach the liquidación PDF, «subir documentos». One Buk assignment per professional and month (created through Buk's assignments API with overwrite_existing_assign), so a re-send UPDATES instead of duplicating. Each professional ends sent, blocked (not sent; reason says what to fix: no confirmed match, unapproved lines, $0) or failed (Buk refused or was unreachable; retry with professional_ids/only_unsent). A blocked professional never blocks another. The liquidación PDF is attached to the Buk employee file when Buk allows it (document_status); a refusal there does not un-send the item. Sent lines are marked exported.

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
periodstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
professional_idsuuid[]—
only_unsentboolean—
curl -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/push \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "period": "2026-09"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "period": "2026-09",
    "results": [
      {
        "id": "b6b6b6b6-0000-4000-8000-000000000021",
        "idempotency_key": "b6b6b6b6-0000-4000-8000-000000000011:2026-09:commission",
        "professional_id": "b6b6b6b6-0000-4000-8000-000000000011",
        "professional_name": "Camila Rojas Soto",
        "period": "2026-09",
        "concept": "commission",
        "amount_clp": 1250000,
        "status": "sent",
        "reason_code": null,
        "reason": null,
        "buk_employee_id": 731,
        "buk_item_id": 4021,
        "buk_assign_id": 90321,
        "document_status": "attached",
        "document_error": null,
        "pushed_by": "00000000-0000-4000-8000-0000000000aa",
        "pushed_at": "2026-10-02T15:30:00.000Z"
      },
      {
        "id": "b6b6b6b6-0000-4000-8000-000000000022",
        "idempotency_key": "b6b6b6b6-0000-4000-8000-000000000012:2026-09:commission",
        "professional_id": "b6b6b6b6-0000-4000-8000-000000000012",
        "professional_name": "Daniel Pino Vega",
        "period": "2026-09",
        "concept": "commission",
        "amount_clp": 480000,
        "status": "blocked",
        "reason_code": "employee_not_matched",
        "reason": "No está vinculado a un colaborador de Buk. Vincúlalo en «Vinculación con Buk».",
        "buk_employee_id": null,
        "buk_item_id": null,
        "buk_assign_id": null,
        "document_status": null,
        "document_error": null,
        "pushed_by": "00000000-0000-4000-8000-0000000000aa",
        "pushed_at": "2026-10-02T15:30:00.000Z"
      }
    ],
    "summary": {
      "sent": 1,
      "blocked": 1,
      "failed": 0,
      "skipped_already_sent": 0,
      "total_sent_clp": 1250000
    }
  }
}

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

GET /clinic/hr/buk/pushes

Buk push log

Who, when, amount and Buk’s outcome per professional, newest first. latest_only=true collapses to the current state of each professional-month.

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
periodquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
latest_onlyquerytrue \falseno
limitqueryintegerno≥ 1, ≤ 500
curl https://api.vitrinadev.com/api/v1/clinic/hr/buk/pushes \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "id": "b6b6b6b6-0000-4000-8000-000000000021",
        "idempotency_key": "b6b6b6b6-0000-4000-8000-000000000011:2026-09:commission",
        "professional_id": "b6b6b6b6-0000-4000-8000-000000000011",
        "professional_name": "Camila Rojas Soto",
        "period": "2026-09",
        "concept": "commission",
        "amount_clp": 1250000,
        "status": "sent",
        "reason_code": null,
        "reason": null,
        "buk_employee_id": 731,
        "buk_item_id": 4021,
        "buk_assign_id": 90321,
        "document_status": "attached",
        "document_error": null,
        "pushed_by": "00000000-0000-4000-8000-0000000000aa",
        "pushed_at": "2026-10-02T15:30:00.000Z"
      }
    ]
  }
}

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

GET /clinic/hr/buk/status

Buk payroll connection status

One Buk (buk.cl) connection per tenant: the company subdomain plus a read-only auth_token API key a human mints inside Buk (Configuración → Accesos API). Every sync reads ONLY the payroll_detail/month operation (Buk has no company-wide aggregate endpoint) and sums the CLOSED settlements in memory — the raw per-employee response (which carries rut) never leaves the provider client. Readable with clinic_money:read.

Connected apps: every patient and contact in the response is a Seudónimo de paciente — initials plus a stable number, "M.F. · #1001" — unless the clinic allowed patient names, with RUT, phone and email masked in free text and meta.patient_privacy saying so. Clinical alerts (flags) are withheld in both modes, and a non-JSON body (an export, a file) is refused with 403 CONNECTED_APP_SENSITIVE_DATA.

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

Ejemplo de respuesta (200)

{
  "data": {
    "connected": true,
    "id": "b6b6b6b6-0000-4000-8000-000000000001",
    "subdomain": "clinicaodm",
    "country": "chile",
    "has_credentials": true,
    "auth_token_preview": "a1b2••••d6",
    "enabled": true,
    "last_sync_at": "2026-09-22T09:05:11.000Z",
    "last_sync_status": "ok",
    "last_sync_error": null,
    "created_at": "2026-08-01T13:00:00.000Z",
    "updated_at": "2026-09-22T09:05:11.000Z"
  }
}

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

POST /clinic/hr/buk/sync

Sincronizar ahora — pull last month’s payroll cost from Buk

Runs the same aggregate pull the daily tick runs, synchronously, for the caller’s tenant. Targets the LAST FULLY-CLOSED calendar month — a liquidación Buk has not closed yet is skipped, not zeroed, and picked up by a later sync. reason explains a 200 that wrote nothing (no_enabled_connection / no_closed_settlements).

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 -X POST https://api.vitrinadev.com/api/v1/clinic/hr/buk/sync \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "period": "2026-08",
    "wrote": true,
    "inserted": false,
    "amount_clp": 5430000,
    "settlement_count": 6,
    "skipped_open_count": 0,
    "skipped_unparsable_count": 0
  }
}

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

En esta página