VitrinaAPI

Clinic HR

The read-only Buk connection and the month's payroll cost, summed from the closed settlements without storing the per-person detail.

Beta
This may change at any time, with a changelog entry and a notice to recent callers — see versioning.

Download the full API pública projection: openapi.json.

MethodPathWhat it does
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/statusBuk payroll connection status
POST/clinic/hr/buk/syncSincronizar ahora — pull last month’s payroll cost from Buk

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.

Body

FieldTypeRequiredConstraints
subdomainstringyesmín. 2, máx. 63, patrón ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
auth_tokenstringyesmí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"
  }'

Example response (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"
  }
}

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

Example response (200)

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

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

Example response (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"
  }
}

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

Example response (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
  }
}

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

On this page