VitrinaAPI

Finanzas

Las finanzas del negocio, para cualquier rubro: bancos, Mercado Pago y SII conectados, con su estado y la última sincronización.

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
GET/finanzas/accountsPlan de cuentas: the account tree
POST/finanzas/accountsAdd an account
DELETE/finanzas/accounts/{id}Delete an unused account
PATCH/finanzas/accounts/{id}Rename, move or retag an account
POST/finanzas/accounts/{id}/archiveArchive an account
POST/finanzas/accounts/{id}/restoreRestore an archived account
GET/finanzas/accounts/reviewPlan de cuentas: the monthly review
POST/finanzas/accounts/review/dismissPlan de cuentas: «No son la misma»
GET/finanzas/ads-chargesMeta billing charges synced, with their link state
POST/finanzas/ads-charges/syncRead the ad data now and match the card charges
GET/finanzas/ads-connectionsAd accounts reachable for Cargos de publicidad
GET/finanzas/ai/metricsIA metrics of a month: calls, auto-applied, undo rate
GET/finanzas/ai/settingsRead the IA switch (Aplicar sugerencias de IA automáticamente)
PUT/finanzas/ai/settingsTurn automatic IA suggestions on or off
GET/finanzas/alertsActive Finanzas alerts
GET/finanzas/automatic-actionsHecho por Vitrina: what Vitrina applied on its own in a month
POST/finanzas/automatic-actions/{kind}/{id}/undoUndo an automatic action (Deshacer)
POST/finanzas/automatic-actions/reviewMark automatic actions reviewed (Marcar revisado)
GET/finanzas/bank-accountsList bank accounts with balances (Cuentas)
POST/finanzas/bank-accountsCreate a manual bank or card account
PUT/finanzas/bank-accounts/{id}/agent-sharingShare a bank account with the AI agent (or stop)
DELETE/finanzas/bank-accounts/{id}/cartola-mappingForget an account's saved column mapping
GET/finanzas/bank-accounts/{id}/cartolasList the statements uploaded to an account
POST/finanzas/bank-accounts/{id}/cartolasImport a statement into Movimientos
POST/finanzas/bank-accounts/{id}/cartolas/previewPreview a statement before importing it
PATCH/finanzas/bank-accounts/{id}/opening-balanceSet a bank account's opening balance
GET/finanzas/cartola-formatsList the statement formats read with no setup
GET/finanzas/casesThe «Por resolver» inbox: open cases, biggest first
GET/finanzas/cases/{id}One case with its proposal
POST/finanzas/cases/{id}/confirmConfirm a case’s proposal («Resolver»)
POST/finanzas/cases/{id}/confirm-editedConfirm an edited proposal
POST/finanzas/cases/{id}/dismissDismiss a «unir» case («No son la misma»)
POST/finanzas/cases/confirm-bulk«Confirmar en bloque»: several ready cases at once
GET/finanzas/cases/count«Te necesita»: how many cases are open
DELETE/finanzas/cases/no-document/{movement_id}Undo «Aceptar sin documento»
GET/finanzas/cash-flowFlujo de caja: actual, projection, band and lowest point
GET/finanzas/cash-flow/calendarPayments calendar: what is due each day, balance after each
POST/finanzas/cash-flow/moves«Mover pagos»: change the planned date of a payment
DELETE/finanzas/cash-flow/moves/{id}Undo a moved payment
GET/finanzas/cash-flow/overdue-no-payment-seen«Vencidas sin pago visto»: overdue payables kept out of the plan
POST/finanzas/cash-flow/overdue-no-payment-seen/resolveMark «Vencidas sin pago visto» as paid
GET/finanzas/cash-flow/recurring-itemsRecurring items of the cash flow
POST/finanzas/cash-flow/recurring-itemsDeclare a recurring item
PATCH/finanzas/cash-flow/recurring-items/{id}Edit a recurring item
POST/finanzas/cash-flow/recurring-items/{id}/end«Terminar» a recurring item
POST/finanzas/cash-flow/rescues«Rescatar fondo»: plan a transfer in from savings or an investment
DELETE/finanzas/cash-flow/rescues/{id}Drop a planned rescue
GET/finanzas/chipaxImportar desde Chipax: connection and runs
DELETE/finanzas/chipax/credentialsImportar desde Chipax: «Eliminar credenciales»
GET/finanzas/chipax/importsImportar desde Chipax: the runs
POST/finanzas/chipax/importsImportar desde Chipax: start a dry run or an import
GET/finanzas/chipax/imports/{id}Importar desde Chipax: one run and its report
GET/finanzas/chipax/shadowMes en paralelo: the months read from Chipax
POST/finanzas/chipax/shadowMes en paralelo: read Chipax's side of a month
GET/finanzas/chipax/shadow/{month}Mes en paralelo: the diff against Chipax for a month
GET/finanzas/chipax/shadow/{month}/exportMes en paralelo: download the diff (xlsx)
GET/finanzas/chipax/shadow/{month}/itemsMes en paralelo: the items behind a row
GET/finanzas/classificationsClassification markers for a page of documents or movements
DELETE/finanzas/classifications/{type}/{id}Unclassify a document or movement
GET/finanzas/classifications/{type}/{id}An item’s classification and its history
PUT/finanzas/classifications/{type}/{id}Classify, split or reclassify a document or movement
POST/finanzas/classifications/bulkClassify many documents or movements to one account
GET/finanzas/connect-setupWhat is connected and what comes next (bank, SII, honorarios)
POST/finanzas/connect-setup/onboardingOnboarding state: start, defer, or «no recibe boletas de honorarios»
POST/finanzas/connect-setup/siiConnect a razón social to the SII (step 1)
POST/finanzas/connect-setup/sii/{id}/mandatoVitrina's mandato for the representante legal to sign (step 3)
GET/finanzas/connect-setup/sii/{id}/mandato/documentThe signed mandato PDF
POST/finanzas/connect-setup/sii/{id}/mandato/revokeRevoke the mandato (cláusula 7.1)
GET/finanzas/connect-setup/sociedad-lookupThe SII's public record of a RUT (razón social prefill)
GET/finanzas/connectionsMoney connections: banks, Mercado Pago and SII
GET/finanzas/connections/{id}/clave-tributariaSII clave status for boletas de honorarios
POST/finanzas/connections/{id}/disconnectDisconnect a bank account
POST/finanzas/connections/{id}/reconnectReconnect a bank account
POST/finanzas/connections/{id}/syncSync a connection now
POST/finanzas/connections/fintocConnect a bank account
POST/finanzas/connections/fintoc/link-intentStart a bank link (Fintoc widget token)
GET/finanzas/connections/syncFreshness of the bank movements
POST/finanzas/connections/syncSync every bank link
GET/finanzas/contador-exportDownload the Contador package for a month (zip)
GET/finanzas/contador-export/manifestWhat the Contador package for a month holds
GET/finanzas/contrapartesList Contrapartes (suppliers and customers)
GET/finanzas/contrapartes/{id}Get a Contraparte
PATCH/finanzas/contrapartes/{id}Rename a Contraparte, or link or unlink it
POST/finanzas/contrapartes/{id}/aliasesLink a RUT or a bank account to a Contraparte
DELETE/finanzas/contrapartes/{id}/aliases/{aliasId}Unlink a RUT, bank account or descriptor from a Contraparte
POST/finanzas/contrapartes/{id}/mergeMerge a duplicate Contraparte into this one
GET/finanzas/contrapartes/{id}/overviewThe Contraparte page: balance, terms, linked accounts, rules
PUT/finanzas/contrapartes/{id}/payment-termsSet a Contraparte's payment terms
GET/finanzas/contrapartes/{id}/timelineA Contraparte's movements and documents, newest first
GET/finanzas/contrapartes/duplicatesProposed Contraparte duplicates («Unir duplicados»)
POST/finanzas/contrapartes/duplicates/dismissReject a proposed duplicate («No son la misma»)
POST/finanzas/contrapartes/duplicates/mergeAccept a proposed duplicate: merge it (undoable)
GET/finanzas/contrapartes/mergesContrapartes merged in a month («Contrapartes unidas»)
POST/finanzas/contrapartes/merges/{mergeId}/undoUndo a Contraparte merge («Deshacer»)
GET/finanzas/costosCostos y equilibrio: fixed vs variable costs and the break-even
GET/finanzas/costos/settingsRead how Costos y equilibrio reads the dentist share
PATCH/finanzas/costos/settingsChange the dentist share accounts or the reference line
GET/finanzas/digest/previewPreview the weekly Finanzas digest
GET/finanzas/documentsList documents (compras, ventas, honorarios, gastos, F29)
GET/finanzas/documents/{id}Get a document
PATCH/finanzas/documents/{id}Edit a manual document
POST/finanzas/documents/{id}/reopenReopen a settled document («Reabrir»)
GET/finanzas/documents/f29F29 by period, with the next due date
GET/finanzas/documents/honorarios/summaryMonthly honorarios and retención total
GET/finanzas/documents/libroMonthly libro de compras or libro de ventas
GET/finanzas/documents/libro/exportExport the libro de compras or ventas (xlsx / csv)
GET/finanzas/documents/uploadsList uploaded and forwarded files
POST/finanzas/documents/uploadsUpload a document that is not in the SII
DELETE/finanzas/documents/uploads/{id}Discard a file
GET/finanzas/documents/uploads/{id}Get an uploaded or forwarded file
POST/finanzas/documents/uploads/{id}/confirmConfirm a file as a document
POST/finanzas/expenses/from-movementsCreate gastos from many bank movements («Crear gasto»)
GET/finanzas/howto-videos«Cómo se usa» videos for the Finanzas screens
GET/finanzas/inboxThe workspace's forwarding address
POST/finanzas/inbox/rotateReplace the forwarding address
GET/finanzas/inherited-overdueVencido heredado (retired): the payables to review
GET/finanzas/inherited-overdue/batchesSettle batches a person made
POST/finanzas/inherited-overdue/batches/{batchId}/undoUndo a settle batch («Deshacer»)
POST/finanzas/inherited-overdue/resolveVencido heredado (retired): mark pagado or anulado in bulk
GET/finanzas/monthsCierre de mes: each month and its state
GET/finanzas/months/{month}Cierre de mes: one month, its close checklist and its close
GET/finanzas/months/{month}/adjustmentsAjustes posteriores al cierre of a month
POST/finanzas/months/{month}/closeClose a month (Cerrar mes)
GET/finanzas/movementsList bank and card movements (Movimientos)
GET/finanzas/movements/{id}Get a movement with its comments
GET/finanzas/movements/{id}/commentsList a movement's comments
POST/finanzas/movements/{id}/commentsComment on a movement
GET/finanzas/own-account-suggestionsOwn-account cards: «Agregar ··XXXX como cuenta propia»
POST/finanzas/own-account-suggestions/{key}/acceptAdd the suggested account as an own account
POST/finanzas/own-account-suggestions/{key}/dismissHide the own-account card for that account («Ahora no»)
GET/finanzas/payment-termsDefault payment terms per document class
PATCH/finanzas/payment-termsChange default payment terms
GET/finanzas/possible-duplicatesPosibles duplicados: bank lines that repeat
GET/finanzas/produccionProducción, cobrado y boleteado (clinics)
GET/finanzas/reconciliation/cash-pendingEfectivo por depositar
GET/finanzas/reconciliation/commissionsPayout commission lines
GET/finanzas/reconciliation/documents/{id}A document's payments and candidates («Asignar pago»)
POST/finanzas/reconciliation/linksLink a movement (Conciliar)
POST/finanzas/reconciliation/links/{id}/undoUndo a link (Deshacer)
GET/finanzas/reconciliation/movements/{id}A movement's reconciliation: links, payer, commission
GET/finanzas/reconciliation/movements/{id}/candidatesCandidates to reconcile a movement («Conciliar»)
POST/finanzas/reconciliation/runRun the reconciliation engine for a period
GET/finanzas/resultadoResultado: the operating result by month
GET/finanzas/resultado/exportExport the resultado (xlsx / csv)
GET/finanzas/resultado/itemsResultado: the documents and movements behind a line
GET/finanzas/resumenResumen: the Finanzas home, three answers and the context
GET/finanzas/rulesReglas: the classification rules
POST/finanzas/rulesCreate a rule
GET/finanzas/rules/{id}A rule
PATCH/finanzas/rules/{id}Edit a rule
POST/finanzas/rules/{id}/apply«Aplicar a pendientes»: apply a rule to unclassified items
POST/finanzas/rules/{id}/archiveArchive a rule
POST/finanzas/rules/{id}/pausePause a rule
GET/finanzas/rules/{id}/pending«Aplicar a pendientes»: preview
POST/finanzas/rules/{id}/resumeResume a paused rule
GET/ops/finanzas-howto-videosManage «Cómo se usa» videos: list every screen
DELETE/ops/finanzas-howto-videos/{screen}Clear a screen's «Cómo se usa» video
PUT/ops/finanzas-howto-videos/{screen}Set or change a screen's «Cómo se usa» video

GET /finanzas/accounts

Plan de cuentas: the account tree

Every account of the workspace as a flat list in tree order (parents before children, siblings in screen order), each with depth and path. The first read of a workspace that holds the add-on creates the starter chart for its vertical (clinic, automotora or generic). Archived accounts are left out unless include_archived=true. With usage_month=YYYY-MM every account gains usage (that month’s net total, document count, last month used and whether it was used in the last 6 months), the same arithmetic as the Resultado; parents roll up their children. usage_month must be a month of the workspace (not after the current one) or the answer is 422 FINANZAS_ACCOUNT_INVALID with details.reason = "usage_month", and it also requires finanzas_reports:read (403 with details.reason = "usage_requires_reports" otherwise). The plan de cuentas is a classification tree, not double-entry. Each account has a kind (ingreso, costo, gasto or no_operacional) and, on costo and gasto only, a behavior (fijo or variable). A sub-account has its parent’s kind. Rule breaks answer 422 FINANZAS_ACCOUNT_INVALID and state conflicts 409 FINANZAS_ACCOUNT_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
include_archivedquerytrue \falseno
usage_monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/accounts \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "accounts": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
        "parent_id": null,
        "name": "Gastos de administración",
        "description": null,
        "kind": "gasto",
        "behavior": "fijo",
        "no_document": false,
        "position": 2,
        "depth": 1,
        "path": "Gastos de administración",
        "seeded": true,
        "has_children": true,
        "archived_at": null,
        "created_at": "2026-10-01T14:03:00.000Z",
        "updated_at": "2026-10-01T14:03:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
        "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
        "name": "Arriendo de oficina",
        "description": "Arriendo mensual de la oficina central.",
        "kind": "gasto",
        "behavior": "fijo",
        "no_document": false,
        "position": 0,
        "depth": 2,
        "path": "Gastos de administración > Arriendo de oficina",
        "seeded": false,
        "has_children": false,
        "archived_at": null,
        "created_at": "2026-10-30T15:20:00.000Z",
        "updated_at": "2026-10-30T15:20:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/accounts

Add an account

Adds an account at the end of its group. A top-level account needs kind; a sub-account takes its parent’s. behavior is required on costo and gasto (a sub-account inherits its parent’s when omitted) and refused elsewhere. Names are unique among live siblings, case-insensitive. At most 5 levels. The plan de cuentas is a classification tree, not double-entry. Each account has a kind (ingreso, costo, gasto or no_operacional) and, on costo and gasto only, a behavior (fijo or variable). A sub-account has its parent’s kind. Rule breaks answer 422 FINANZAS_ACCOUNT_INVALID and state conflicts 409 FINANZAS_ACCOUNT_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Arriendo de oficina",
    "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "description": "Arriendo mensual de la oficina central."
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
    "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "name": "Arriendo de oficina",
    "description": "Arriendo mensual de la oficina central.",
    "kind": "gasto",
    "behavior": "fijo",
    "no_document": false,
    "position": 0,
    "depth": 2,
    "path": "Gastos de administración > Arriendo de oficina",
    "seeded": false,
    "has_children": false,
    "archived_at": null,
    "created_at": "2026-10-30T15:20:00.000Z",
    "updated_at": "2026-10-30T15:20:00.000Z"
  }
}

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

DELETE /finanzas/accounts/{id}

Delete an unused account

Only an account nothing was classified under can be deleted. With sub-accounts it answers 409 has_children; with classified documents or movements 409 has_history: archive it instead. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/accounts/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

PATCH /finanzas/accounts/{id}

Rename, move or retag an account

Change name, description, behavior (costo and gasto only), no_document («No lleva documento»: the account opens no «Falta documento» case and its money out is not counted in «Egresos sin documento»), position (order among siblings) or move the account with parent_id (null = top level). A move keeps the account’s id, so every classification follows it; it is refused under itself or its sub-accounts (cycle), under another kind (kind_mismatch) or beyond 5 levels (too_deep). The kind itself never changes. An archived account cannot be edited. The plan de cuentas is a classification tree, not double-entry. Each account has a kind (ingreso, costo, gasto or no_operacional) and, on costo and gasto only, a behavior (fijo or variable). A sub-account has its parent’s kind. Rule breaks answer 422 FINANZAS_ACCOUNT_INVALID and state conflicts 409 FINANZAS_ACCOUNT_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/accounts/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Arriendo de oficina central",
    "position": 1
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
    "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "name": "Arriendo de oficina central",
    "description": "Arriendo mensual de la oficina central.",
    "kind": "gasto",
    "behavior": "fijo",
    "no_document": false,
    "position": 1,
    "depth": 2,
    "path": "Gastos de administración > Arriendo de oficina",
    "seeded": false,
    "has_children": false,
    "archived_at": null,
    "created_at": "2026-10-30T15:20:00.000Z",
    "updated_at": "2026-10-30T15:20:00.000Z"
  }
}

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

POST /finanzas/accounts/{id}/archive

Archive an account

Hides the account from pickers; it stays in the data and in reports. Refused while it has live sub-accounts (has_active_children). Idempotent. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts/<id>/archive \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
    "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "name": "Arriendo de oficina",
    "description": "Arriendo mensual de la oficina central.",
    "kind": "gasto",
    "behavior": "fijo",
    "no_document": false,
    "position": 0,
    "depth": 2,
    "path": "Gastos de administración > Arriendo de oficina",
    "seeded": false,
    "has_children": false,
    "archived_at": "2026-10-30T16:00:00.000Z",
    "created_at": "2026-10-30T15:20:00.000Z",
    "updated_at": "2026-10-30T16:00:00.000Z"
  }
}

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

POST /finanzas/accounts/{id}/restore

Restore an archived account

Brings an archived account back. Its parent must be live (parent_archived). Idempotent. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts/<id>/restore \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
    "parent_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "name": "Arriendo de oficina",
    "description": "Arriendo mensual de la oficina central.",
    "kind": "gasto",
    "behavior": "fijo",
    "no_document": false,
    "position": 0,
    "depth": 2,
    "path": "Gastos de administración > Arriendo de oficina",
    "seeded": false,
    "has_children": false,
    "archived_at": null,
    "created_at": "2026-10-30T15:20:00.000Z",
    "updated_at": "2026-10-30T15:20:00.000Z"
  }
}

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

GET /finanzas/accounts/review

Plan de cuentas: the monthly review

Is the plan in order? How much of the month’s outflow has an account (classified_pct), the documents nobody classified (the Resultado’s «Egresos sin clasificar»), and up to three findings in priority order: unclassified, duplicate_import (accounts an import brought that repeat a template account) and unused (leaf accounts with no money in 6 months). Default month: the latest closed month, else the previous month, else the current one (in_progress). A month outside the workspace answers 422 FINANZAS_ACCOUNT_INVALID (details.reason = "month"). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read and finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/accounts/review \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-10",
    "in_progress": false,
    "classified_pct": 95.7,
    "outflow_total_clp": 37000000,
    "unclassified": {
      "count": 7,
      "total_clp": 1590984,
      "by_kind": [
        {
          "kind": "purchase",
          "count": 7,
          "total_clp": 1590984
        }
      ],
      "with_proposal": 4
    },
    "unclassified_income": {
      "count": 0,
      "total_clp": 0
    },
    "findings": [
      {
        "key": "unclassified",
        "count": 7,
        "total_clp": 1590984
      },
      {
        "key": "duplicate_import",
        "count": 1,
        "pairs": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d03",
            "name": "Ret. Boletas de Honorarios (auto)",
            "duplicates_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d04",
            "duplicates_name": "Retención de boletas de honorarios",
            "used_6m": true
          }
        ]
      }
    ]
  }
}

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

POST /finanzas/accounts/review/dismiss

Plan de cuentas: «No son la misma»

Stops a duplicate_import pair from appearing in the review again. Idempotent. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts/review/dismiss \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "duplicate_import",
    "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d03"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "dismissed": true
  }
}

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

GET /finanzas/ads-charges

Meta billing charges synced, with their link state

Every billing event (charge, refund, chargeback, decline, funding) of the workspace's Meta ad accounts whose day (account time zone) is in [from, to], oldest first, never deduplicated by amount. linked = matched to the ads document of a card movement. Amounts in the smallest unit of currency. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
curl https://api.vitrinadev.com/api/v1/finanzas/ads-charges \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "from": "2026-10-22",
    "to": "2026-10-22",
    "items": [
      {
        "id": "0193a1b2-0000-7000-8000-000000000001",
        "platform": "meta_ads",
        "ad_account_id": "act_969938652650502",
        "event_type": "ad_account_billing_charge",
        "event_label": "cargo",
        "event_time": "2026-10-22T12:14:00.000Z",
        "local_date": "2026-10-22",
        "meta_transaction_id": "29140315238986117-29184038214613815",
        "amount_minor": 4211,
        "currency": "CLP",
        "link_state": "linked",
        "document_id": "0193a1b2-0000-7000-8000-0000000000d1",
        "movement_id": "0193a1b2-0000-7000-8000-0000000000a1",
        "linked_at": "2026-10-24T09:00:00.000Z"
      }
    ],
    "totals": {
      "count": 1,
      "linked": 1,
      "unlinked": 0
    }
  }
}

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

POST /finanzas/ads-charges/sync

Read the ad data now and match the card charges

«Sincronizar ahora»: recognise new ad charges among the card movements, read the last 92 days of ad billing and spend, store the charges and link what the evidence supports; then the reconciliation engine and «Por resolver» follow. A failed read is recorded (read = error, shown in GET /finanzas/ads-connections) and the stored charges still decide what they can. A card movement whose descriptor names Google Ads (GOOGLE *ADS&lt;customer id&gt;) or Meta (FACEBK *&lt;ref&gt;, META*…, META PLATFORMS…) becomes a manual_expense document of Contraparte «Google Ads» / «Meta Platforms» (GET /finanzas/documents?origin=ads); a refund (money in) a nota de crédito. Vitrina links it when the ad data makes it plausible (Meta: the billing charge of the same amount, count for count in time order; Google and Meta without charge data: the account spend since its previous charge must cover the amount), shown in «Hecho por Vitrina» with the evidence; the rest waits in «Por resolver» (case kind ads_charge). Vitrina never holds a Meta or Google token. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Cuerpo

CampoTipoObligatorioRestricciones
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/ads-charges/sync \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ejemplo de respuesta (200)

{
  "data": {
    "skipped": false,
    "read": "ok",
    "accounts": 1,
    "charges_new": 3,
    "recognize": {
      "recognized": 4,
      "created": 4,
      "receipt_first": 0
    },
    "match": {
      "open": 4,
      "linked": 3,
      "unconfirmed": 1,
      "no_connection": 0,
      "pending": 0
    }
  }
}

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

GET /finanzas/ads-connections

Ad accounts reachable for Cargos de publicidad

Per platform (Meta Ads, Google Ads): the ad accounts connected to the workspace, their status, last sync and the charges synced and linked, from the last read of the ad data. Each platform says what happens without a connection; connecting is Integraciones › Meta Ads. A card movement whose descriptor names Google Ads (GOOGLE *ADS&lt;customer id&gt;) or Meta (FACEBK *&lt;ref&gt;, META*…, META PLATFORMS…) becomes a manual_expense document of Contraparte «Google Ads» / «Meta Platforms» (GET /finanzas/documents?origin=ads); a refund (money in) a nota de crédito. Vitrina links it when the ad data makes it plausible (Meta: the billing charge of the same amount, count for count in time order; Google and Meta without charge data: the account spend since its previous charge must cover the amount), shown in «Hecho por Vitrina» with the evidence; the rest waits in «Por resolver» (case kind ads_charge). Vitrina never holds a Meta or Google token. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/ads-connections \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "profile": true,
    "last_attempt_at": "2026-10-31T12:30:00.000Z",
    "last_sync_at": "2026-10-31T12:30:00.000Z",
    "last_error": null,
    "platforms": [
      {
        "platform": "meta_ads",
        "label": "Meta Ads",
        "status": "connected",
        "accounts": [
          {
            "platform": "meta_ads",
            "ad_account_id": "act_969938652650502",
            "account_label": "act_969938652650502",
            "account_name": "connectmarketing-cl",
            "currency": "CLP",
            "timezone": "America/Santiago",
            "connection_status": "connected",
            "healthy": true,
            "last_synced_at": "2026-10-31T06:00:58.984Z",
            "charges_supported": true,
            "charges_synced_at": "2026-10-31T12:26:04.120Z",
            "charges_synced": 40,
            "charges_linked": 38,
            "charges_unreconciled": 2,
            "spend_by_month": [
              {
                "month": "2026-10",
                "spend_minor": 545869,
                "currency": "CLP"
              }
            ]
          }
        ],
        "without_connection": "Sin la conexión, los cargos de Meta quedan en Por resolver hasta que reenvíes el recibo.",
        "connect_path": "/settings/integrations"
      },
      {
        "platform": "google_ads",
        "label": "Google Ads",
        "status": "none",
        "accounts": [],
        "without_connection": "Sin la conexión, los cargos de Google Ads quedan en Por resolver hasta que reenvíes el recibo.",
        "connect_path": "/settings/integrations"
      }
    ]
  }
}

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

GET /finanzas/ai/metrics

IA metrics of a month: calls, auto-applied, undo rate

IA calls in the month (one open item each), how many were applied on their own, proposed or left without an answer, and how many of the applied ones a person undid since (undo_rate = undone / auto_applied, null when none were applied). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthquerystringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/ai/metrics \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-10",
    "calls": 42,
    "auto_applied": 18,
    "proposed": 15,
    "no_answer": 9,
    "undone": 1,
    "undo_rate": 0.0556,
    "input_tokens": 37800
  }
}

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

GET /finanzas/ai/settings

Read the IA switch (Aplicar sugerencias de IA automáticamente)

After every bank, Mercado Pago or SII sync, the IA looks only at what the engine, the rules and the deterministic proposals left open, and never at patient data. A confident answer is applied on its own (marked IA, undoable in Hecho por Vitrina; an undo teaches it); otherwise it becomes the Por resolver proposal (reason ia). auto_apply is on by default. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/ai/settings \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "auto_apply": true,
    "updated_at": null,
    "updated_by": null
  }
}

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

PUT /finanzas/ai/settings

Turn automatic IA suggestions on or off

After every bank, Mercado Pago or SII sync, the IA looks only at what the engine, the rules and the deterministic proposals left open, and never at patient data. A confident answer is applied on its own (marked IA, undoable in Hecho por Vitrina; an undo teaches it); otherwise it becomes the Por resolver proposal (reason ia). With auto_apply: false the IA keeps answering, but its answers only become Por resolver proposals a person confirms. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Cuerpo

CampoTipoObligatorioRestricciones
auto_applybooleansí
curl -X PUT https://api.vitrinadev.com/api/v1/finanzas/ai/settings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_apply": false
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "auto_apply": false,
    "updated_at": "2026-10-30T21:10:00.000Z",
    "updated_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01"
  }
}

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

GET /finanzas/alerts

Active Finanzas alerts

The alerts whose condition still holds: cash_negative (the Flujo de caja projection drops below zero in the next 30 days), f29_due (an F29 with something to pay is due within 5 days, unpaid) and supplier_invoice_missing (a Contraparte that invoiced each of the last 3 months has nothing this month, 3 days past its usual day). Evaluated hourly from 09:00 workspace time; each event is announced once to the owners and admins (bell and email by default; WhatsApp only on the opt-in finanzas_whatsapp rail) and leaves this list when its condition ends. Newest first. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise) and finanzas:read; the cash alert is listed only with finanzas_reports:read too.

curl https://api.vitrinadev.com/api/v1/finanzas/alerts \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f8a01",
      "kind": "f29_due",
      "title": "El F29 de octubre vence el 20 de noviembre",
      "body": "Hay que pagar $842.000 (monto estimado) al SII antes del 20 de noviembre. Cuando el pago aparezca en tus movimientos, el F29 queda como pagado.",
      "cta_label": "Ver F29",
      "path": "/finanzas/documentos?vista=f29",
      "opened_on": "2026-11-15",
      "notified_at": "2026-11-15T12:00:04.000Z",
      "updated_at": "2026-11-16T12:00:03.000Z",
      "data": {
        "period": "2026-10",
        "due_date": "2026-11-20",
        "amount_clp": 842000,
        "days_left": 4,
        "amount_is_final": false,
        "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01"
      }
    }
  ]
}

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

GET /finanzas/automatic-actions

Hecho por Vitrina: what Vitrina applied on its own in a month

Every automatic action whose item falls in the month (the movement day, the document's issue date), grouped by Contraparte (own-account transfers, cash deposits and patient payments have their own group), biggest total first. An action is a reconciliation link by the engine (actor match Coincidencia, rule Regla, ai IA) or a classification by a rule or the IA that is still current. Undone actions and links a person caused («Crear gasto») are left out. Each group lists at most 100 items (items_truncated); pass group for all of one group. rules_created lists the rules Vitrina created this month from repeated confirmations. unreviewed_count is what the month close reports. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthquerystringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
groupquerystringnopatrón ^(?:contraparte:[0-9a-f-]{36}|own_transfers|cash_deposits|clinic_payments|none)$
reviewedqueryall \unreviewedno
curl https://api.vitrinadev.com/api/v1/finanzas/automatic-actions \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-10",
    "from": "2026-10-01",
    "to": "2026-10-31",
    "count": 2,
    "total_clp": 1640000,
    "unreviewed_count": 1,
    "by_actor": {
      "rule": 1,
      "match": 1,
      "ai": 0
    },
    "rules_created": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
        "label": "Documentos (compras) de Proveedor Ejemplo SpA → Costos directos › Laboratorio",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
          "name": "Proveedor Ejemplo SpA"
        },
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
          "name": "Laboratorio",
          "path": "Costos directos › Laboratorio"
        },
        "status": "active",
        "created_at": "2026-10-04T15:30:00.000Z"
      }
    ],
    "groups": [
      {
        "key": "contraparte:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
          "name": "Proveedor Ejemplo SpA"
        },
        "name": "Proveedor Ejemplo SpA",
        "count": 2,
        "total_clp": 1640000,
        "unreviewed_count": 1,
        "items": [
          {
            "kind": "link",
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e01",
            "actor": "match",
            "label": "Conciliado con Factura N° 4521",
            "day": "2026-10-29",
            "amount_clp": 1190000,
            "applied_at": "2026-10-30T09:12:00.000Z",
            "movement": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
              "day": "2026-10-29",
              "direction": "outbound",
              "amount_clp": 1190000,
              "description": "TRANSF A PROVEEDOR EJEMPLO SPA FACT 4521"
            },
            "document": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
              "kind": "purchase",
              "dte_type": 33,
              "folio": "4521",
              "issue_date": "2026-10-01",
              "total_clp": 1190000,
              "counterparty_name": "Proveedor Ejemplo SpA"
            },
            "target": "document",
            "reasons": [
              "folio_and_amount"
            ],
            "own_transfer": null,
            "account": null,
            "split": false,
            "rule": null,
            "reviewed_at": null,
            "reviewed_by": null
          },
          {
            "kind": "classification",
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e02",
            "actor": "rule",
            "label": "Clasificado en Costos directos › Laboratorio",
            "day": "2026-10-05",
            "amount_clp": 450000,
            "applied_at": "2026-10-06T08:00:00.000Z",
            "movement": null,
            "document": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
              "kind": "purchase",
              "dte_type": 33,
              "folio": "4410",
              "issue_date": "2026-10-05",
              "total_clp": 450000,
              "counterparty_name": "Proveedor Ejemplo SpA"
            },
            "target": null,
            "reasons": [],
            "own_transfer": null,
            "account": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
              "name": "Laboratorio",
              "path": "Costos directos › Laboratorio"
            },
            "split": false,
            "rule": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
              "name": null
            },
            "reviewed_at": "2026-10-07T13:00:00.000Z",
            "reviewed_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01"
          }
        ],
        "items_truncated": false
      }
    ]
  }
}

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

POST /finanzas/automatic-actions/{kind}/{id}/undo

Undo an automatic action (Deshacer)

Undoes one automatic action; its item is open again and returns to «Por resolver». A link is stamped undone (both sides get the amount back; the engine never re-applies that pair). A classification is removed with an audited unclassify by the person, and the rule never re-applies to that item. Undoing an IA action stores the teaching signal (teaching_signal: true) the IA reads as a counter-example. 404 when the id is not an automatic action; 409 when already undone or when the classification changed afterwards. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
kindpathlinks \classificationssí
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/automatic-actions/<id>/<id>/undo \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "kind": "link",
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e01",
    "actor": "match",
    "item": {
      "type": "movement",
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10"
    },
    "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
    "teaching_signal": false
  }
}

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

POST /finanzas/automatic-actions/review

Mark automatic actions reviewed (Marcar revisado)

Marks actions reviewed: the listed items, or every unreviewed action of a month (with group, only that group). Idempotent: already-reviewed actions are left as they are; reviewed counts the ones stamped now. A person-made link or classification is never counted. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

Cuerpo

CampoTipoObligatorioRestricciones
itemsobject[]—
monthstring—patrón ^\d{4}-(0[1-9]|1[0-2])$
groupstring—patrón ^(?:contraparte:[0-9a-f-]{36}|own_transfers|cash_deposits|clinic_payments|none)$
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/automatic-actions/review \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "month": "2026-10",
    "group": "contraparte:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "reviewed": 12
  }
}

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

GET /finanzas/bank-accounts

List bank accounts with balances (Cuentas)

Each account's current balance and the total in banks today (the Resumen's «Caja»). When the bank feed reports a balance, that is it (balance_source = feed); otherwise it is the opening balance plus the movements after its day (computed), or unknown without one. USD balances convert at the latest dólar observado. total_clp sums the known balances and as_of is the oldest of their times. These are BANK accounts, not the plan de cuentas. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/bank-accounts \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "accounts": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "name": "Cuenta corriente",
        "institution_name": "Banco de Chile",
        "number_masked": "****4821",
        "type": "cuenta_corriente",
        "currency": "CLP",
        "source": "manual",
        "has_feed": false,
        "balance_clp": 8450000,
        "balance_original": null,
        "balance_as_of": "2026-10-30T08:00:00.000Z",
        "balance_source": "computed",
        "opening_balance": {
          "amount_minor": 5000000,
          "on": "2026-09-30"
        },
        "opening_balance_editable": true,
        "movement_count": 312,
        "agent_sharing": {
          "shared": true,
          "alias": "Banco de Chile ··4821",
          "holder_name": "Clínica Ejemplo SpA",
          "holder_rut": "76123456-7",
          "holder_email": null,
          "account_type": "corriente",
          "holder_not_company": false
        },
        "can_share_with_agent": true
      }
    ],
    "total_clp": 8450000,
    "as_of": "2026-10-30T08:00:00.000Z",
    "unknown_count": 0
  }
}

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

POST /finanzas/bank-accounts

Create a manual bank or card account

An account without a bank feed (a credit card, a bank Fintoc does not cover): its movements arrive by uploading the bank statement to /finanzas/bank-accounts/\{id\}/cartolas. Shows in Cuentas with source = manual; its balance is the opening balance plus the movements. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/bank-accounts \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Visa empresa",
    "institution_name": "Banco de Chile",
    "number": "4111111111114821",
    "type": "tarjeta_credito"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0195f2c8-7d3e-7a10-9b4e-2a6f3c1d8e55",
    "name": "Visa empresa",
    "institution_name": "Banco de Chile",
    "number_masked": "•••• 4821",
    "type": "tarjeta_credito",
    "currency": "CLP",
    "source": "manual",
    "has_feed": false,
    "balance_clp": -1250000,
    "balance_original": null,
    "balance_as_of": "2026-10-28T00:00:00.000Z",
    "balance_source": "computed",
    "opening_balance": {
      "amount_minor": -400000,
      "on": "2026-09-30"
    },
    "opening_balance_editable": true,
    "movement_count": 14
  }
}

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

PUT /finanzas/bank-accounts/{id}/agent-sharing

Share a bank account with the AI agent (or stop)

One bank-account registry (Finanzas 33): the Finanzas bank accounts are the workspace's accounts, and «el agente comparte esta cuenta» is a flag on one of them. shared: true makes the AI agent dictate this account for transfers and matches abonos into it (the same registry GET /tenant-bank-accounts lists); false stops it, keeping the row. The first share needs holder_name (400 VALIDATION_ERROR, details.reason = agent_sharing_holder_required); holder_rut defaults to the workspace's SII RUT. 409 CONFLICT with details.reason = agent_sharing_not_supported for a card, a credit line, a USD account or a number known only by its last digits. Turning sharing on for a holder whose RUT is not the company's needs confirm_holder_not_company: true (409 CONFLICT, details.reason = agent_sharing_holder_not_company, naming the holder); only send the flag after the human has explicitly agreed to share an account held by someone other than the company, and never retry the call with it on your own; an account already shared is never blocked or unshared. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PUT https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/agent-sharing \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "shared": true,
    "holder_name": "Clínica Ejemplo SpA"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
    "name": "Cuenta corriente",
    "institution_name": "Banco de Chile",
    "number_masked": "****4821",
    "type": "cuenta_corriente",
    "currency": "CLP",
    "source": "manual",
    "has_feed": false,
    "balance_clp": 8450000,
    "balance_original": null,
    "balance_as_of": "2026-10-30T08:00:00.000Z",
    "balance_source": "computed",
    "opening_balance": {
      "amount_minor": 5000000,
      "on": "2026-09-30"
    },
    "opening_balance_editable": true,
    "movement_count": 312,
    "agent_sharing": {
      "shared": true,
      "alias": "Banco de Chile ··4821",
      "holder_name": "Clínica Ejemplo SpA",
      "holder_rut": "76123456-7",
      "holder_email": null,
      "account_type": "corriente",
      "holder_not_company": false
    },
    "can_share_with_agent": true
  }
}

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

DELETE /finanzas/bank-accounts/{id}/cartola-mapping

Forget an account's saved column mapping

The next upload is read automatically again (or asks for a new mapping). Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartola-mapping \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/bank-accounts/{id}/cartolas

List the statements uploaded to an account

Newest first (up to 50): file name, format, range, counts, balances and any accepted gap. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartolas \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0195f2c9-0a11-7c20-8d6f-91b7e4a3c2d1",
      "bank_account_id": "0195f2c8-7d3e-7a10-9b4e-2a6f3c1d8e55",
      "file_name": "cartola-octubre.csv",
      "format": "banco_de_chile",
      "date_from": "2026-10-01",
      "date_to": "2026-10-28",
      "rows_read": 14,
      "rows_new": 14,
      "rows_duplicate": 0,
      "rows_rejected": 0,
      "opening_minor": -400000,
      "closing_minor": -1250000,
      "gap_minor": 0,
      "gap_accepted": false,
      "created_at": "2026-10-30T14:02:11.000Z"
    }
  ]
}

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

POST /finanzas/bank-accounts/{id}/cartolas

Import a statement into Movimientos

Imports the statement of a manual account: its movements land in Movimientos like any other (source = cartola), on their Contraparte. Idempotent: re-uploading a file, or an overlapping one, adds nothing twice (counts.duplicate).

Continuity check. The file's opening balance must equal the account's balance in Vitrina at the end of the day before the file starts. On a difference the import answers 409 CONFLICT with details.reason = cartola_gap and details.continuity (amount, dates) and imports nothing; repeat with accept_gap = true to import anyway (the account balance is restated to the bank's). A file whose own balance column does not add up answers 400 with details.reason = cartola_balance_inconsistent. The first file of a new account sets the account's opening balance from the file. A file without a balance column (most card statements) is imported without a continuity check (continuity.status = not_checked).

An unknown layout needs mapping; it is saved on the account (save_mapping, default true) and reused. multipart/form-data (file, optional mapping JSON text, save_mapping, accept_gap) or the JSON file shape. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
filenamestringsímín. 1, máx. 255
contentstringsímín. 1, máx. 35000000
content_encodingutf8 \base64—
content_typestring—máx. 200
mappingobject | null—
save_mappingboolean—
accept_gapboolean—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartolas \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "(binary file part)"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "import": {
      "id": "0195f2c9-0a11-7c20-8d6f-91b7e4a3c2d1",
      "file_name": "cartola-octubre.csv",
      "format": "banco_de_chile",
      "date_from": "2026-10-01",
      "date_to": "2026-10-28",
      "created_at": "2026-10-30T14:02:11.000Z"
    },
    "counts": {
      "rows_read": 14,
      "new": 14,
      "duplicate": 0,
      "rejected": 0,
      "skipped": 1
    },
    "rejections": [],
    "continuity": {
      "status": "ok",
      "reason": null,
      "file_opening_minor": -400000,
      "file_opening_on": "2026-09-30",
      "ledger_balance_minor": -400000,
      "gap_minor": 0,
      "gap_from": null,
      "gap_to": null
    },
    "gap_accepted": false,
    "closing_gap_minor": 0,
    "mapping_saved": false,
    "account": {
      "id": "0195f2c8-7d3e-7a10-9b4e-2a6f3c1d8e55",
      "name": "Visa empresa",
      "institution_name": "Banco de Chile",
      "number_masked": "•••• 4821",
      "type": "tarjeta_credito",
      "currency": "CLP",
      "source": "manual",
      "has_feed": false,
      "balance_clp": -1250000,
      "balance_original": null,
      "balance_as_of": "2026-10-28T00:00:00.000Z",
      "balance_source": "computed",
      "opening_balance": {
        "amount_minor": -400000,
        "on": "2026-09-30"
      },
      "opening_balance_editable": true,
      "movement_count": 14
    }
  }
}

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

POST /finanzas/bank-accounts/{id}/cartolas/preview

Preview a statement before importing it

Reads the statement and says what an import would do, writing nothing: the format, the movements (with which are already in Vitrina), the date range, the balances and the continuity check (the file's opening balance against the account's balance in Vitrina; continuity.status = gap carries the amount and the dates). When the layout is not recognised the answer is status = needs_mapping with the header row, a sample and what was recognised: map the columns and send the preview again with mapping. Only for a manual account (409 details.reason = cartola_account_has_feed otherwise).

multipart/form-data (file, optional mapping as JSON text) or the JSON file shape. Amounts are the account currency smallest unit (CLP pesos, USD cents). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
filenamestringsímín. 1, máx. 255
contentstringsímín. 1, máx. 35000000
content_encodingutf8 \base64—
content_typestring—máx. 200
mappingobject | null—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartolas/preview \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "(binary file part)"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "status": "ready",
    "mapping_needed": null,
    "format": {
      "key": "banco_de_chile",
      "label": "Banco de Chile",
      "auto": true
    },
    "mapping_source": "auto",
    "header_row": 8,
    "columns": {
      "date": "Fecha",
      "description": "Descripción",
      "amount": "Monto"
    },
    "date_from": "2026-10-01",
    "date_to": "2026-10-28",
    "counts": {
      "rows_read": 14,
      "new": 14,
      "duplicate": 0,
      "rejected": 0,
      "skipped": 1
    },
    "totals": {
      "inbound_minor": 150000,
      "outbound_minor": 1000000
    },
    "balance": {
      "has_balance": false,
      "opening_minor": null,
      "closing_minor": null,
      "chain_breaks": []
    },
    "continuity": {
      "status": "not_checked",
      "reason": "no_balance_in_file",
      "file_opening_minor": -400000,
      "file_opening_on": "2026-09-30",
      "ledger_balance_minor": -400000,
      "gap_minor": 0,
      "gap_from": null,
      "gap_to": null
    },
    "rejections": [],
    "movements": [
      {
        "row": 9,
        "date": "2026-10-02",
        "direction": "outbound",
        "amount_minor": 45000,
        "description": "COMPRA UBER TRIP",
        "balance_minor": null,
        "duplicate": false
      }
    ],
    "movements_truncated": false,
    "currency": "CLP"
  }
}

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

PATCH /finanzas/bank-accounts/{id}/opening-balance

Set a bank account's opening balance

For an account whose feed reports no balance (cartola uploads, Mercado Pago): amount_minor is the balance at the END of day on, in the account currency smallest unit (CLP pesos, USD cents; negative for a credit line); null clears it. 409 when the bank reports the balance. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/opening-balance \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": 5000000,
    "on": "2026-09-30"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
    "name": "Cuenta corriente",
    "institution_name": "Banco de Chile",
    "number_masked": "****4821",
    "type": "cuenta_corriente",
    "currency": "CLP",
    "source": "manual",
    "has_feed": false,
    "balance_clp": 8450000,
    "balance_original": null,
    "balance_as_of": "2026-10-30T08:00:00.000Z",
    "balance_source": "computed",
    "opening_balance": {
      "amount_minor": 5000000,
      "on": "2026-09-30"
    },
    "opening_balance_editable": true,
    "movement_count": 312,
    "agent_sharing": {
      "shared": true,
      "alias": "Banco de Chile ··4821",
      "holder_name": "Clínica Ejemplo SpA",
      "holder_rut": "76123456-7",
      "holder_email": null,
      "account_type": "corriente",
      "holder_not_company": false
    },
    "can_share_with_agent": true
  }
}

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

GET /finanzas/cartola-formats

List the statement formats read with no setup

The banks whose cartola (CSV / xlsx) parses without a column mapping. Any other layout whose header is recognisable still parses; a layout that is not asks for a one-time mapping on upload. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/cartola-formats \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "key": "banco_de_chile",
      "label": "Banco de Chile",
      "bank": "Banco de Chile"
    },
    {
      "key": "santander",
      "label": "Santander",
      "bank": "Banco Santander"
    }
  ]
}

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

GET /finanzas/cases

The «Por resolver» inbox: open cases, biggest first

A case is the open money of one Contraparte that Vitrina could not resolve on its own: bank movements nothing explains, cost documents with no account, and money out classified with no document («Falta documento»). Cases are recomputed after every bank, Mercado Pago and SII sync and before every read; only cases whose inputs changed are rewritten. Sorted by the money at stake (order=needs_person: the cases a person must answer first). kind, month (YYYY-MM: a case belongs to ONE month, its oldest item's) and period (deprecated: recent / history) filter; total and amount_at_stake_clp cover every open case of that filter, counts_by_month lists every month with open cases (the month filter's options; months «Cerrado antes de Vitrina» hold none), counts splits them (needs_person / ready) and waiting lists the payments «esperando factura» (no case yet). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
kindqueryreconcile \transfer \classify \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0, ≤ 100000
periodqueryrecent \history \all
monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
orderqueryamount \needs_personno
curl https://api.vitrinadev.com/api/v1/finanzas/cases \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
        "kind": "reconcile",
        "title": "Laboratorio Dental Andes Ltda",
        "summary": "1 cargo ($775.290) vs 2 facturas",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
          "name": "Laboratorio Dental Andes Ltda",
          "rut": "76123456-7"
        },
        "amount_at_stake_clp": 775290,
        "item_count": 1,
        "movement_count": 1,
        "document_count": 2,
        "ready": true,
        "opened_at": "2026-10-22T09:00:00.000Z",
        "latest_day": "2026-10-21",
        "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
        "kind": "missing_document",
        "title": "Maestro jardinero",
        "summary": "1 egreso sin documento ($80.000)",
        "contraparte": null,
        "amount_at_stake_clp": 80000,
        "item_count": 1,
        "movement_count": 1,
        "document_count": 0,
        "ready": true,
        "opened_at": "2026-10-23T09:00:00.000Z",
        "latest_day": "2026-10-20",
        "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e"
      }
    ],
    "total": 2,
    "amount_at_stake_clp": 855290,
    "since": "2026-09-01",
    "counts_by_month": [
      {
        "month": "2026-10",
        "open": 2,
        "amount_clp": 855290
      }
    ],
    "counts": {
      "recent": 2,
      "history": 0,
      "needs_person": 0,
      "ready": 2
    },
    "waiting": {
      "count": 0,
      "amount_clp": 0,
      "items": []
    }
  }
}

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

GET /finanzas/cases/{id}

One case with its proposal

The case’s movements (role open | missing_document), documents (role unclassified | candidate: what the proposal pays plus a few more of the Contraparte to swap in), days of «efectivo por depositar», the proposed actions (link | classify | accept_without_document, each with stable reasons), the Spanish why, and the items with no proposal (unresolved; ready is false then). A resolved case still reads (status, resolution confirmed | cleared). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/cases/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
    "key": "contraparte:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
    "kind": "reconcile",
    "status": "open",
    "resolution": null,
    "resolved_at": null,
    "contraparte": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
      "name": "Laboratorio Dental Andes Ltda",
      "rut": "76123456-7"
    },
    "title": "Laboratorio Dental Andes Ltda",
    "summary": "1 cargo ($775.290) vs 2 facturas",
    "amount_at_stake_clp": 775290,
    "item_count": 1,
    "ready": true,
    "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e",
    "movements": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
        "day": "2026-10-21",
        "direction": "outbound",
        "amount_clp": 775290,
        "pending_clp": 775290,
        "description": "TRANSF A LABORATORIO DENTAL ANDES",
        "counterparty_name": "Laboratorio Dental Andes Ltda",
        "bank_account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
          "name": "Cuenta corriente"
        },
        "role": "open",
        "classification": null
      }
    ],
    "documents": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
        "kind": "purchase",
        "dte_type": 33,
        "folio": "18204",
        "issue_date": "2026-09-12",
        "due_date": "2026-10-12",
        "total_clp": 412690,
        "gross_clp": 412690,
        "outstanding_clp": 412690,
        "counterparty_name": "Laboratorio Dental Andes Ltda",
        "role": "candidate",
        "classified": true
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
        "kind": "purchase",
        "dte_type": 33,
        "folio": "18377",
        "issue_date": "2026-09-26",
        "due_date": "2026-10-26",
        "total_clp": 362600,
        "gross_clp": 362600,
        "outstanding_clp": 362600,
        "counterparty_name": "Laboratorio Dental Andes Ltda",
        "role": "candidate",
        "classified": true
      }
    ],
    "cash_days": [],
    "proposal": {
      "actions": [
        {
          "type": "link",
          "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
          "links": [
            {
              "target": "document",
              "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
              "amount_clp": 412690
            },
            {
              "target": "document",
              "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02",
              "amount_clp": 362600
            }
          ],
          "replace_classification": false,
          "reasons": [
            "sum_of_documents"
          ]
        }
      ],
      "why": [
        "La suma de 2 facturas es exacta: $775.290, el cargo del 21 oct"
      ]
    },
    "unresolved": [],
    "opened_at": "2026-10-22T09:00:00.000Z",
    "computed_at": "2026-10-22T09:00:00.000Z"
  }
}

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

POST /finanzas/cases/{id}/confirm

Confirm a case’s proposal («Resolver»)

Applies the case’s own proposal, which must cover every item (ready; else 422 FINANZAS_CASE_INVALID proposal_incomplete with details.items). ONE transaction: every link (the Conciliar rules: amounts, directions, clinic money authority), classification (the classification rules) and «Aceptar sin documento» lands, or none does; a refused step answers with that step’s own error (400 VALIDATION_ERROR, 409 CONFLICT, 422 FINANZAS_CLASSIFICATION_INVALID …). Links are written with actor person (the caller), undoable from the movement. Returns the next case (next_case_id) for J/K triage and the phone’s «siguiente». 409 FINANZAS_CASE_CHANGED (details.reason fingerprint_mismatch | resolved, details.case = the case as it is now, or null): nothing was written; show the fresh case and confirm again. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500

Cuerpo

CampoTipoObligatorioRestricciones
fingerprintstringsímín. 1, máx. 128
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cases/<id>/confirm \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
    "applied": {
      "links": 2,
      "classifications": 0,
      "accepted": 0
    },
    "remaining_case": null,
    "next_case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
    "descriptors": []
  }
}

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

POST /finanzas/cases/{id}/confirm-edited

Confirm an edited proposal

The person’s version of the proposal: swap a document, change the account, accept a cargo without document, or confirm only part of the case (what is left stays open as remaining_case). Every movement or classified item must belong to the case (422 item_not_in_case; one action per item, duplicate_item); a link may target any document. replace_classification (a «Falta documento» whose document arrived) removes the classification before linking. ONE transaction: every link (the Conciliar rules: amounts, directions, clinic money authority), classification (the classification rules) and «Aceptar sin documento» lands, or none does; a refused step answers with that step’s own error (400 VALIDATION_ERROR, 409 CONFLICT, 422 FINANZAS_CLASSIFICATION_INVALID …). Links are written with actor person (the caller), undoable from the movement. Returns the next case (next_case_id) for J/K triage and the phone’s «siguiente». 409 FINANZAS_CASE_CHANGED (details.reason fingerprint_mismatch | resolved, details.case = the case as it is now, or null): nothing was written; show the fresh case and confirm again. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500

Cuerpo

CampoTipoObligatorioRestricciones
fingerprintstring—mín. 1, máx. 128
actionsany[]sí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cases/<id>/confirm-edited \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e",
    "actions": [
      {
        "type": "link",
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
        "links": [
          {
            "target": "document",
            "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d02"
          }
        ]
      },
      {
        "type": "classify",
        "item": {
          "type": "document",
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01"
        },
        "account_name": "Laboratorio"
      }
    ]
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
    "applied": {
      "links": 1,
      "classifications": 1,
      "accepted": 0
    },
    "remaining_case": null,
    "next_case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
    "descriptors": []
  }
}

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

POST /finanzas/cases/{id}/dismiss

Dismiss a «unir» case («No son la misma»)

Finanzas 58, merge cases only (422 not_a_merge_case otherwise): the two Contrapartes are not the same. The pair is never proposed or merged again and the case closes. To merge them instead, use the confirm route. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cases/<id>/dismiss \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
    "next_case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02"
  }
}

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

POST /finanzas/cases/confirm-bulk

«Confirmar en bloque»: several ready cases at once

Confirms each case’s own proposal (every case must be ready), with the fingerprint the screen read it with. Each case is its own transaction: a changed, resolved, incomplete or refused case stays open and is listed in failed with its reason; the rest are confirmed. At most 100 per call. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cases/confirm-bulk \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cases": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
        "fingerprint": "9c1e4b7a2f0d5c8e1b3a6f9d2c5e8b1a4d7f0c3e"
      }
    ]
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "confirmed": [
      {
        "case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
        "applied": {
          "links": 1,
          "classifications": 0,
          "accepted": 0
        }
      }
    ],
    "failed": []
  }
}

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

GET /finanzas/cases/count

«Te necesita»: how many cases are open

The one count of open work (Finanzas 47): open cases, the money at stake and the count per kind. Resumen, Por resolver, Movimientos, Cierre de mes and the weekly digest show this same number for the same scope. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/cases/count \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "open": 2,
    "amount_at_stake_clp": 855290,
    "by_kind": {
      "reconcile": 1,
      "transfer": 0,
      "classify": 0,
      "missing_document": 1,
      "unknown": 0
    }
  }
}

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

DELETE /finanzas/cases/no-document/{movement_id}

Undo «Aceptar sin documento»

The cargo asks for its document again: its «Falta documento» case reopens (case_id). Its classification is untouched. 404 when it was never accepted. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
movement_idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cases/no-document/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
    "case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02"
  }
}

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

GET /finanzas/cash-flow

Flujo de caja: actual, projection, band and lowest point

Per day (or ISO week): what came in and went out (actual, before today) and what is expected (projected, today on), with the closing balance of each period. The projection starts from today's balance of the operating accounts (every bank account except savings accounts) and adds known items on their dates: open payables and receivables by due date (outstanding = total − retención − live reconciliation links), the retención of honorarios received (due with the F29 on the 20th of the next month), recurring items, patient cuotas, booked appointments with a price, the efectivo por depositar (cash_pending, spread over its expected deposit days) and planned rescues. That is the CONFIRMED balance (closing_balance_clp), the one every headline reads. A payable past its due date with no matching payment in the data is a «Vencida sin pago visto»: it stays out (overdue_no_payment_seen, the list at GET /finanzas/cash-flow/overdue-no-payment-seen) unless it was given a date with «Mover pagos»; overdue receivables stay out too (overdue). A document «Pagado antes de Vitrina» is settled and never projected (inherited_overdue is retired, always zero). The lowest point (lowest) includes today's balance and is never above it (lowest.is_today). expected_balance_clp («con cobros esperados», the dashed line) adds, from tomorrow, each day's typical income: the median of that weekday's bank income over the last 90 days, counted only above the day's known income. band is retired and always null. Past days are actual bank movements; transfers between operating accounts are neither in nor out. lowest is the lowest confirmed balance in the range (its cause = the payment at which the confirmed balance first goes below zero, else the biggest payment of that day); month_end is the projected balance at the end of the current month («¿cuánto me queda a fin de mes?»), computed even when to is earlier. Range: up to a year back and a year ahead, at most 366 days. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
horizon_daysqueryintegerno≥ 1, ≤ 365
granularityqueryday \weekno
curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "as_of": "2026-10-30",
    "from": "2026-10-29",
    "to": "2026-11-01",
    "granularity": "day",
    "opening": {
      "balance_clp": 4917109,
      "as_of": "2026-10-30T08:00:00.000Z",
      "accounts_count": 4,
      "unknown_count": 0,
      "savings": [
        {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a02",
          "name": "Cuenta de ahorro",
          "institution_name": "Banco Estado",
          "balance_clp": 1200000
        }
      ],
      "savings_clp": 1200000
    },
    "periods": [
      {
        "start": "2026-10-29",
        "end": "2026-10-29",
        "kind": "actual",
        "inflow_clp": 2412000,
        "typical_inflow_clp": 0,
        "outflow_clp": 698400,
        "closing_balance_clp": 4617109,
        "expected_balance_clp": 4617109,
        "band": null,
        "items_count": 0
      },
      {
        "start": "2026-10-30",
        "end": "2026-10-30",
        "kind": "projected",
        "inflow_clp": 120000,
        "typical_inflow_clp": 0,
        "outflow_clp": 412690,
        "closing_balance_clp": 4624419,
        "expected_balance_clp": 4624419,
        "band": null,
        "items_count": 2
      },
      {
        "start": "2026-10-31",
        "end": "2026-10-31",
        "kind": "projected",
        "inflow_clp": 0,
        "typical_inflow_clp": 1150000,
        "outflow_clp": 0,
        "closing_balance_clp": 4624419,
        "expected_balance_clp": 5774419,
        "band": null,
        "items_count": 0
      },
      {
        "start": "2026-11-01",
        "end": "2026-11-01",
        "kind": "projected",
        "inflow_clp": 0,
        "typical_inflow_clp": 0,
        "outflow_clp": 0,
        "closing_balance_clp": 4624419,
        "expected_balance_clp": 5774419,
        "band": null,
        "items_count": 0
      }
    ],
    "lowest": {
      "date": "2026-10-30",
      "balance_clp": 4624419,
      "negative": false,
      "is_today": false,
      "cause": null,
      "cause_balance_after_clp": null
    },
    "month_end": {
      "date": "2026-11-30",
      "balance_clp": 2310000,
      "expected_balance_clp": 9410000,
      "band": null
    },
    "totals": {
      "actual_inflow_clp": 2412000,
      "actual_outflow_clp": 698400,
      "projected_inflow_clp": 120000,
      "typical_inflow_clp": 1150000,
      "projected_outflow_clp": 412690,
      "by_source": {
        "payable": 412690,
        "receivable": 0,
        "retention": 0,
        "recurring": 0,
        "cuotas": 0,
        "appointments": 0,
        "cash_pending": 120000,
        "rescue": 0
      }
    },
    "overdue": {
      "payables_clp": 412690,
      "payables_count": 1,
      "stale_payables_clp": 0,
      "stale_payables_count": 0,
      "receivables_clp": 0,
      "receivables_count": 0
    },
    "overdue_no_payment_seen": {
      "count": 1,
      "amount_clp": 412690
    },
    "inherited_overdue": {
      "count": 0,
      "amount_clp": 0
    },
    "cash_pending": {
      "amount_clp": 11648107,
      "window_days": 60,
      "expected_until": "2026-11-06"
    },
    "typical_income": {
      "basis": "history",
      "weeks": 12
    },
    "band_basis": {
      "from": "2026-08-01",
      "to": "2026-10-29",
      "weekdays": [
        {
          "weekday": 0,
          "min_clp": 0,
          "median_clp": 0,
          "max_clp": 0
        },
        {
          "weekday": 1,
          "min_clp": 1720000,
          "median_clp": 2380000,
          "max_clp": 3010000
        },
        {
          "weekday": 2,
          "min_clp": 1640000,
          "median_clp": 2350000,
          "max_clp": 2960000
        },
        {
          "weekday": 3,
          "min_clp": 1810000,
          "median_clp": 2400000,
          "max_clp": 3120000
        },
        {
          "weekday": 4,
          "min_clp": 1700000,
          "median_clp": 2390000,
          "max_clp": 2990000
        },
        {
          "weekday": 5,
          "min_clp": 1590000,
          "median_clp": 2310000,
          "max_clp": 3200000
        },
        {
          "weekday": 6,
          "min_clp": 670000,
          "median_clp": 1150000,
          "max_clp": 1480000
        }
      ]
    },
    "rescues": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7d01",
        "planned_on": "2026-11-25",
        "amount_clp": 5100000,
        "source_name": "Fondo mutuo Itaú",
        "source_bank_account_id": null,
        "note": null,
        "created_at": "2026-10-30T15:00:00.000Z"
      }
    ],
    "moves_count": 1
  }
}

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

GET /finanzas/cash-flow/calendar

Payments calendar: what is due each day, balance after each

The projected items from today, grouped by day, each with the expected balance right after it (money in first, then the biggest payments). The day's typical income lands after its items. direction keeps only money in or only payments (balances still count everything). The projection starts from today's balance of the operating accounts (every bank account except savings accounts) and adds known items on their dates: open payables and receivables by due date (outstanding = total − retención − live reconciliation links), the retención of honorarios received (due with the F29 on the 20th of the next month), recurring items, patient cuotas, booked appointments with a price, the efectivo por depositar (cash_pending, spread over its expected deposit days) and planned rescues. That is the CONFIRMED balance (closing_balance_clp), the one every headline reads. A payable past its due date with no matching payment in the data is a «Vencida sin pago visto»: it stays out (overdue_no_payment_seen, the list at GET /finanzas/cash-flow/overdue-no-payment-seen) unless it was given a date with «Mover pagos»; overdue receivables stay out too (overdue). A document «Pagado antes de Vitrina» is settled and never projected (inherited_overdue is retired, always zero). The lowest point (lowest) includes today's balance and is never above it (lowest.is_today). expected_balance_clp («con cobros esperados», the dashed line) adds, from tomorrow, each day's typical income: the median of that weekday's bank income over the last 90 days, counted only above the day's known income. band is retired and always null. Past days are actual bank movements; transfers between operating accounts are neither in nor out. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
directionqueryinflow \outflowno
curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow/calendar \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "as_of": "2026-10-30",
    "from": "2026-10-30",
    "to": "2026-11-30",
    "opening_balance_clp": 4917109,
    "days": [
      {
        "date": "2026-11-26",
        "opening_balance_clp": 15752058,
        "items": [
          {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null,
            "balance_after_clp": -1840000
          }
        ],
        "typical_inflow_clp": 0,
        "closing_balance_clp": -1840000
      },
      {
        "date": "2026-12-01",
        "opening_balance_clp": 2310000,
        "items": [
          {
            "key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
            "source": "payable",
            "direction": "outflow",
            "label": "Factura 18377 · Laboratorio Dental Andes Ltda",
            "amount_clp": 362600,
            "date": "2026-12-01",
            "due_date": "2026-11-13",
            "overdue": false,
            "movable": true,
            "move": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7c01",
              "original_on": "2026-11-13",
              "note": null
            },
            "document": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
              "kind": "purchase",
              "dte_type": 33,
              "folio": "18377"
            },
            "contraparte": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6a01",
              "name": "Laboratorio Dental Andes Ltda"
            },
            "recurring_item_id": null,
            "rescue_id": null,
            "count": null,
            "balance_after_clp": 1947400
          }
        ],
        "typical_inflow_clp": 2380000,
        "closing_balance_clp": 4327400
      }
    ],
    "lowest": {
      "date": "2026-11-26",
      "balance_clp": -1840000,
      "negative": true,
      "is_today": false,
      "cause": {
        "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
        "source": "recurring",
        "direction": "outflow",
        "label": "Sueldos",
        "amount_clp": 17592058,
        "date": "2026-11-26",
        "due_date": "2026-11-26",
        "overdue": false,
        "movable": true,
        "move": null,
        "document": null,
        "contraparte": null,
        "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
        "rescue_id": null,
        "count": null
      },
      "cause_balance_after_clp": -1840000
    }
  }
}

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

POST /finanzas/cash-flow/moves

«Mover pagos»: change the planned date of a payment

Re-dates one movable projected item (key from the calendar: a document, a recurring occurrence or a retención) to planned_on (today to a year ahead). Moving it to its own date removes the move. It changes the plan only: a document keeps its due date and no bank is touched. Answers the lowest point and month-end balance before and after. ?dry_run=true answers the effect and saves nothing. An item the projection no longer holds (paid, ended) answers 404. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
dry_runquerytrue \falseno
horizon_daysqueryintegerno≥ 1, ≤ 365
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cash-flow/moves \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "item_key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
    "planned_on": "2026-12-01",
    "note": "Pagamos con el abono del convenio"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "dry_run": false,
    "move": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7c01",
      "item_key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
      "original_on": "2026-11-13",
      "planned_on": "2026-12-01",
      "note": "Pagamos con el abono del convenio",
      "updated_at": "2026-10-30T15:10:00.000Z"
    },
    "item": {
      "key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
      "label": "Factura 18377 · Laboratorio Dental Andes Ltda",
      "amount_clp": 362600,
      "due_date": "2026-11-13",
      "planned_on": "2026-12-01"
    },
    "effect": {
      "before": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": -1840000,
          "negative": true,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 2310000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "after": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": 3260000,
          "negative": false,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 7410000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "horizon": {
        "days": 30,
        "to": "2026-11-30"
      }
    }
  }
}

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

DELETE /finanzas/cash-flow/moves/{id}

Undo a moved payment

The item goes back to its own date. It changes the plan only: a document keeps its due date and no bank is touched. Answers the effect. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
horizon_daysqueryintegerno≥ 1, ≤ 365
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cash-flow/moves/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "removed": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7c01",
      "item_key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
      "original_on": "2026-11-13",
      "planned_on": "2026-12-01",
      "note": "Pagamos con el abono del convenio",
      "updated_at": "2026-10-30T15:10:00.000Z"
    },
    "effect": {
      "before": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": 3260000,
          "negative": false,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 7410000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "after": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": -1840000,
          "negative": true,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 2310000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "horizon": {
        "days": 30,
        "to": "2026-11-30"
      }
    }
  }
}

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

GET /finanzas/cash-flow/overdue-no-payment-seen

«Vencidas sin pago visto»: overdue payables kept out of the plan

Payables past their due date with no matching payment in the data Vitrina has (it does not claim they are unpaid: a card whose data stops earlier may have paid them), and no date given with «Mover pagos». They stay out of the projection and every headline. Each can be matched to a payment (Conciliación), marked paid (POST /finanzas/cash-flow/overdue-no-payment-seen/resolve) or given a date (POST /finanzas/cash-flow/moves with its key). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow/overdue-no-payment-seen \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "as_of": "2026-11-09",
    "count": 1,
    "amount_clp": 405369,
    "items": [
      {
        "key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
        "source": "payable",
        "direction": "outflow",
        "label": "Factura 21619609 · Empresa Nacional de Telecomunicaciones S.A.",
        "amount_clp": 405369,
        "date": "2026-08-31",
        "due_date": "2026-08-31",
        "overdue": true,
        "movable": true,
        "move": null,
        "document": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01",
          "kind": "purchase",
          "dte_type": 33,
          "folio": "21619609"
        },
        "contraparte": null,
        "recurring_item_id": null,
        "rescue_id": null,
        "count": null
      }
    ]
  }
}

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

POST /finanzas/cash-flow/overdue-no-payment-seen/resolve

Mark «Vencidas sin pago visto» as paid

The person knows these were paid (by card, cash, outside the banks Vitrina reads): each document leaves the list as paid. No bank link is invented. A document that is no longer an overdue open payable is skipped (not_eligible), never overwritten. One batch, one batch_id, audited as finanzas.overdue_no_payment_seen.pagado; POST /finanzas/inherited-overdue/batches/{batchId}/undo puts the batch back; POST /finanzas/documents/{id}/reopen reopens one. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cash-flow/overdue-no-payment-seen/resolve \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document_ids": [
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01"
    ],
    "note": "Pagada con la tarjeta Itaú"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "batch_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7e01",
    "resolved": [
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7a01"
    ],
    "skipped": [],
    "amount_clp": 405369,
    "remaining": {
      "count": 0,
      "amount_clp": 0
    }
  }
}

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

GET /finanzas/cash-flow/recurring-items

Recurring items of the cash flow

Declared recurring inflows and outflows (rent, payroll, a credit instalment, taxes) with their next occurrence; ended ones last. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow/recurring-items \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
      "name": "Sueldos",
      "direction": "outflow",
      "amount_clp": 17592058,
      "cadence": "monthly",
      "starts_on": "2026-01-26",
      "ends_on": null,
      "account": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d20",
        "name": "Sueldos",
        "kind": "gasto",
        "archived": false
      },
      "contraparte": null,
      "note": null,
      "summary": "Mensual · $17.592.058",
      "status": "active",
      "next_on": "2026-11-26",
      "created_at": "2026-10-30T14:00:00.000Z",
      "updated_at": "2026-10-30T14:00:00.000Z"
    }
  ]
}

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

POST /finanzas/cash-flow/recurring-items

Declare a recurring item

Amount (whole CLP), cadence, plan de cuentas account (not archived), first day and optional end. With contraparte_id, an occurrence is skipped when a document of that Contraparte falls due within half a period of it (the document stands for it). Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cash-flow/recurring-items \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sueldos",
    "direction": "outflow",
    "amount_clp": 17592058,
    "cadence": "monthly",
    "starts_on": "2026-01-26",
    "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d20"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
    "name": "Sueldos",
    "direction": "outflow",
    "amount_clp": 17592058,
    "cadence": "monthly",
    "starts_on": "2026-01-26",
    "ends_on": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d20",
      "name": "Sueldos",
      "kind": "gasto",
      "archived": false
    },
    "contraparte": null,
    "note": null,
    "summary": "Mensual · $17.592.058",
    "status": "active",
    "next_on": "2026-11-26",
    "created_at": "2026-10-30T14:00:00.000Z",
    "updated_at": "2026-10-30T14:00:00.000Z"
  }
}

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

PATCH /finanzas/cash-flow/recurring-items/{id}

Edit a recurring item

Send only what changes. The whole series follows (past occurrences are not projected anyway); ends_on: null reopens an ended item. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/cash-flow/recurring-items/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_clp": 18100000
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
    "name": "Sueldos",
    "direction": "outflow",
    "amount_clp": 18100000,
    "cadence": "monthly",
    "starts_on": "2026-01-26",
    "ends_on": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d20",
      "name": "Sueldos",
      "kind": "gasto",
      "archived": false
    },
    "contraparte": null,
    "note": null,
    "summary": "Mensual · $18.100.000",
    "status": "active",
    "next_on": "2026-11-26",
    "created_at": "2026-10-30T14:00:00.000Z",
    "updated_at": "2026-10-30T14:00:00.000Z"
  }
}

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

POST /finanzas/cash-flow/recurring-items/{id}/end

«Terminar» a recurring item

No occurrence after ends_on (default: today; the day before it starts when it has not started). Nothing is deleted. An item that already ended answers 409. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cash-flow/recurring-items/<id>/end \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ends_on": "2026-12-31"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
    "name": "Sueldos",
    "direction": "outflow",
    "amount_clp": 17592058,
    "cadence": "monthly",
    "starts_on": "2026-01-26",
    "ends_on": "2026-12-31",
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d20",
      "name": "Sueldos",
      "kind": "gasto",
      "archived": false
    },
    "contraparte": null,
    "note": null,
    "summary": "Mensual · $17.592.058",
    "status": "active",
    "next_on": "2026-11-26",
    "created_at": "2026-10-30T14:00:00.000Z",
    "updated_at": "2026-10-30T14:00:00.000Z"
  }
}

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

POST /finanzas/cash-flow/rescues

«Rescatar fondo»: plan a transfer in from savings or an investment

Adds a planned inflow on planned_on (today to a year ahead). Name its source (source_name, e.g. «Fondo mutuo Itaú») or pick a savings account (source_bank_account_id, type: cuenta_ahorro; its name is the default source). An operating account answers 400: its money is already in the balance. It changes the plan only: a document keeps its due date and no bank is touched. Answers the effect; ?dry_run=true saves nothing (200 instead of 201). Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
dry_runquerytrue \falseno
horizon_daysqueryintegerno≥ 1, ≤ 365
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cash-flow/rescues \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "planned_on": "2026-11-25",
    "amount_clp": 5100000,
    "source_name": "Fondo mutuo Itaú"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "dry_run": true,
    "rescue": null,
    "effect": {
      "before": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": -1840000,
          "negative": true,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 2310000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "after": {
        "lowest": {
          "date": "2026-11-26",
          "balance_clp": 3260000,
          "negative": false,
          "is_today": false,
          "cause": {
            "key": "recurring:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01:2026-11-26",
            "source": "recurring",
            "direction": "outflow",
            "label": "Sueldos",
            "amount_clp": 17592058,
            "date": "2026-11-26",
            "due_date": "2026-11-26",
            "overdue": false,
            "movable": true,
            "move": null,
            "document": null,
            "contraparte": null,
            "recurring_item_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7b01",
            "rescue_id": null,
            "count": null
          },
          "cause_balance_after_clp": -1840000
        },
        "month_end": {
          "date": "2026-11-30",
          "balance_clp": 7410000,
          "expected_balance_clp": 9410000,
          "band": null
        }
      },
      "horizon": {
        "days": 30,
        "to": "2026-11-30"
      }
    }
  }
}

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

DELETE /finanzas/cash-flow/rescues/{id}

Drop a planned rescue

Removes a planned «Rescatar fondo». It changes the plan only: a document keeps its due date and no bank is touched. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:write (the Contador seat reads, never writes).

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cash-flow/rescues/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "removed": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7d01",
      "planned_on": "2026-11-25",
      "amount_clp": 5100000,
      "source_name": "Fondo mutuo Itaú",
      "source_bank_account_id": null,
      "note": null,
      "created_at": "2026-10-30T15:00:00.000Z"
    }
  }
}

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

GET /finanzas/chipax

Importar desde Chipax: connection and runs

Whether the workspace stored its Chipax API credentials (only a masked App ID hint, never the secret), the run in progress and the last finished one. Vitrina only READS Chipax: it logs in and sends GET requests; it never creates, edits or deletes anything in the Chipax account. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/chipax \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connected": true,
    "app_id_preview": "a1b…9f2",
    "verified_at": "2026-10-30T21:58:40.000Z",
    "active_import": null,
    "last_import": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a16",
      "mode": "dry_run",
      "status": "done",
      "from": "2025-01-01",
      "to": "2026-10-30",
      "account_map": [],
      "requested_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
      "error": null,
      "started_at": "2026-10-30T22:01:04.000Z",
      "finished_at": "2026-10-30T22:14:51.000Z",
      "created_at": "2026-10-30T22:01:02.000Z",
      "report": null
    }
  }
}

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

DELETE /finanzas/chipax/credentials

Importar desde Chipax: «Eliminar credenciales»

Deletes the stored Chipax credentials. What was already imported stays. Idempotent. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/chipax/credentials \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connected": false,
    "app_id_preview": null,
    "verified_at": null,
    "active_import": null,
    "last_import": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a16",
      "mode": "dry_run",
      "status": "done",
      "from": "2025-01-01",
      "to": "2026-10-30",
      "account_map": [],
      "requested_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
      "error": null,
      "started_at": "2026-10-30T22:01:04.000Z",
      "finished_at": "2026-10-30T22:14:51.000Z",
      "created_at": "2026-10-30T22:01:02.000Z",
      "report": null
    }
  }
}

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

GET /finanzas/chipax/imports

Importar desde Chipax: the runs

The last 20 runs, newest first (no report; GET one for it). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/chipax/imports \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a16",
      "mode": "dry_run",
      "status": "done",
      "from": "2025-01-01",
      "to": "2026-10-30",
      "account_map": [],
      "requested_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
      "error": null,
      "started_at": "2026-10-30T22:01:04.000Z",
      "finished_at": "2026-10-30T22:14:51.000Z",
      "created_at": "2026-10-30T22:01:02.000Z",
      "report": null
    }
  ]
}

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

POST /finanzas/chipax/imports

Importar desde Chipax: start a dry run or an import

Queues one run (202); poll GET /finanzas/chipax/imports/\{id\}. Reading Chipax takes minutes (its rate limit: one filtered request every 5 s per endpoint). dry_run writes nothing but the report: counts per entity (create / match / already / skip with reasons), the chart mapping with the accounts it cannot decide, and a totals check against Chipax's Resultado Operacional per account and month. import does it: idempotent (a re-run changes nothing), documents the SII already gave Vitrina are matched on (issuer RUT, DTE type, folio) and enriched, never duplicated, and every write is automatic (actor import), so a closed month accepts it and logs it as an «ajuste posterior al cierre». Pass account_map with the person's choices from the dry-run report. One run at a time. Vitrina only READS Chipax: it logs in and sends GET requests; it never creates, edits or deletes anything in the Chipax account. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Cuerpo

CampoTipoObligatorioRestricciones
modedry_run \importsí
fromstring—patrón ^\d{4}-\d{2}-\d{2}$
tostring—patrón ^\d{4}-\d{2}-\d{2}$
account_mapobject[]—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/chipax/imports \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "import",
    "from": "2025-01-01",
    "to": "2026-10-30",
    "account_map": [
      {
        "chipax_account": 5110,
        "action": "map",
        "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c12"
      },
      {
        "chipax_account": 5207,
        "action": "create"
      }
    ]
  }'

Ejemplo de respuesta (202)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a16",
    "mode": "import",
    "status": "queued",
    "from": "2025-01-01",
    "to": "2026-10-30",
    "account_map": [
      {
        "chipax_account": 5110,
        "action": "map",
        "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c12"
      },
      {
        "chipax_account": 5207,
        "action": "create",
        "account_id": null
      }
    ],
    "requested_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
    "error": null,
    "started_at": null,
    "finished_at": null,
    "created_at": "2026-10-30T22:01:02.000Z",
    "report": null
  }
}

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

GET /finanzas/chipax/imports/{id}

Importar desde Chipax: one run and its report

The run and its report (partial while running; phase says where it is). In a dry run the counts say what an import would do. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/imports/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a16",
    "mode": "dry_run",
    "status": "done",
    "from": "2025-01-01",
    "to": "2026-10-30",
    "account_map": [],
    "requested_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
    "error": null,
    "started_at": "2026-10-30T22:01:04.000Z",
    "finished_at": "2026-10-30T22:14:51.000Z",
    "created_at": "2026-10-30T22:01:02.000Z",
    "report": {
      "phase": "done",
      "dry_run": true,
      "range": {
        "from": "2025-01-01",
        "to": "2026-10-30"
      },
      "read": {
        "accounts": 59,
        "clients": 3,
        "compra": 1240,
        "dte": 18,
        "honorario": 310,
        "boleta_tercero": 0,
        "gasto": 42,
        "remuneracion": 96,
        "previred": 22,
        "impuesto": 22,
        "cartolas": 2665,
        "ro_rows": 1950
      },
      "accounts": {
        "create": 4,
        "match": 54,
        "already": 0,
        "skip": 1,
        "skip_reasons": {
          "ambiguous": 1
        },
        "items": [
          {
            "chipax_account": 5101,
            "name": "Laboratorio",
            "chipax_path": "Costos › Laboratorio",
            "kind": "costo",
            "action": "map",
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c11",
            "account_path": "Costos de explotación › Laboratorios",
            "chipax_parent_account": 5100,
            "basis": "same_name",
            "candidates": []
          },
          {
            "chipax_account": 5207,
            "name": "Arriendo bodega",
            "chipax_path": "Gastos › Arriendo bodega",
            "kind": "gasto",
            "action": "create",
            "account_id": null,
            "account_path": null,
            "chipax_parent_account": 5200,
            "basis": "new",
            "candidates": []
          },
          {
            "chipax_account": 5110,
            "name": "Dentistas",
            "chipax_path": "Costos › Dentistas",
            "kind": "costo",
            "action": "skip",
            "account_id": null,
            "account_path": null,
            "chipax_parent_account": 5100,
            "basis": "ambiguous",
            "candidates": [
              {
                "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c12",
                "path": "Costos de explotación › Dentistas"
              },
              {
                "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c13",
                "path": "Costos de explotación › Profesionales › Dentistas"
              }
            ]
          }
        ]
      },
      "contrapartes": {
        "create": 37,
        "match": 112,
        "already": 0,
        "skip": 0,
        "skip_reasons": {}
      },
      "documents": {
        "create": 180,
        "match": 1530,
        "already": 0,
        "skip": 0,
        "skip_reasons": {},
        "by_model": {
          "compra": 1240,
          "honorario": 310,
          "gasto": 42,
          "remuneracion": 96,
          "previred": 22,
          "impuesto": 22,
          "dte": 18
        }
      },
      "classifications": {
        "create": 1650,
        "match": 0,
        "already": 0,
        "skip": 42,
        "skip_reasons": {
          "classified_in_vitrina": 12,
          "unmapped_account": 30
        },
        "scaled": 1180
      },
      "links": {
        "create": 2210,
        "match": 0,
        "already": 0,
        "skip": 310,
        "skip_reasons": {
          "movement_not_found": 290,
          "movement_ambiguous": 20
        }
      },
      "movement_classifications": {
        "create": 64,
        "match": 0,
        "already": 0,
        "skip": 0,
        "skip_reasons": {}
      },
      "rules": {
        "create": 41,
        "match": 2,
        "already": 0,
        "skip": 0,
        "skip_reasons": {},
        "items": [
          {
            "rut": "76086428-5",
            "name": "Laboratorio Dental Andes Ltda",
            "document_kind": "purchase",
            "chipax_account": 5101,
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c11",
            "documents": 48
          }
        ]
      },
      "payment_terms": {
        "create": 9,
        "match": 1,
        "already": 0,
        "skip": 0,
        "skip_reasons": {},
        "items": [
          {
            "rut": "76086428-5",
            "days": 30,
            "documents": 48
          }
        ]
      },
      "aliases": {
        "create": 6,
        "match": 0,
        "already": 0,
        "skip": 1,
        "skip_reasons": {
          "rut_is_another_contraparte": 1
        }
      },
      "unmapped": [
        {
          "type": "account",
          "chipax_ref": "5110",
          "label": "Costos › Dentistas",
          "reason": "ambiguous"
        },
        {
          "type": "link",
          "chipax_ref": "88123:compra:44120",
          "label": "2025-03-04 cargo $410000",
          "reason": "movement_not_found"
        }
      ],
      "totals_check": {
        "available": true,
        "mismatches": 1,
        "explained": 1,
        "explained_by_code": {
          "redondeo": -1
        },
        "rows": [
          {
            "chipax_account": 5110,
            "account_id": null,
            "month": "2026-09",
            "chipax_clp": 6120000,
            "imported_chipax_clp": 0,
            "vitrina_clp": 0,
            "difference_clp": -6120000,
            "explanations": [],
            "unexplained_clp": -6120000,
            "status": "mismatch",
            "matches": false
          },
          {
            "chipax_account": 5101,
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c11",
            "month": "2026-09",
            "chipax_clp": 2380000,
            "imported_chipax_clp": 2380000,
            "vitrina_clp": 2379999,
            "difference_clp": -1,
            "explanations": [
              {
                "code": "redondeo",
                "amount_clp": -1
              }
            ],
            "unexplained_clp": 0,
            "status": "explained",
            "matches": true
          }
        ]
      },
      "not_in_api": [
        "Las automatizaciones (reglas) de Chipax no están en su API: se infieren del historial (un proveedor que siempre fue a la misma cuenta)."
      ],
      "warnings": []
    }
  }
}

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

GET /finanzas/chipax/shadow

Mes en paralelo: the months read from Chipax

Each month with a Chipax read, newest first: its newest read (any status) and when it was last read successfully. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read and finanzas_reports:read (the Contador seat, owners and admins).

curl https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "month": "2026-10",
      "label": "octubre 2026",
      "latest_read": {
        "id": "01930000-0000-7000-8000-000000000017",
        "month": "2026-10",
        "status": "done",
        "history_from": "2025-01-01",
        "error": null,
        "requested_by": "00000000-0000-4000-8000-000000000001",
        "started_at": null,
        "finished_at": null,
        "created_at": "2026-11-02T12:00:00.000Z"
      },
      "read_at": "2026-11-02T12:04:10.000Z"
    }
  ]
}

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

POST /finanzas/chipax/shadow

Mes en paralelo: read Chipax's side of a month

Queues a read of Chipax for the month (bank lines and balances, open payables, classified items, Resultado Operacional), over the documents issued since history_from. Takes minutes (Chipax's rate limit). One read at a time; refused while a Chipax import runs (409 import_running) or another read runs (409 read_running); 409 not_connected without Chipax credentials; 409 chipax_credential_unreadable when the stored credential cannot be decrypted (connect Chipax again). Vitrina only READS Chipax: it logs in and sends GET requests; it never creates, edits or deletes anything in the Chipax account. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Cuerpo

CampoTipoObligatorioRestricciones
monthstring—patrón ^\d{4}-(0[1-9]|1[0-2])$
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "month": "2026-10"
  }'

Ejemplo de respuesta (202)

{
  "data": {
    "id": "01930000-0000-7000-8000-000000000017",
    "month": "2026-10",
    "status": "queued",
    "history_from": "2025-01-01",
    "error": null,
    "requested_by": "00000000-0000-4000-8000-000000000001",
    "started_at": null,
    "finished_at": null,
    "created_at": "2026-11-02T12:00:00.000Z"
  }
}

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

GET /finanzas/chipax/shadow/{month}

Mes en paralelo: the diff against Chipax for a month

Balances (per bank account, at month end), payables (per Contraparte, open at month end) and costs (per costo / gasto account, NET as the Resultado counts) must match to the peso. Income is a bridge: Chipax counts it when cashed, Vitrina the documents issued in the month, so the gap is explained line by line. Vitrina's side is computed live; Chipax's side is the newest finished read. clean = all three match and the bridge is explained (the exit criterion). Each row's key drills down to its items. Rows list the differences first; difference_count of each section counts its rows that are not «Cuadra», and the headline difference_count is the sum of the sections with in_headline (the review possible_duplicates is listed beside them and never counted in it; inherited_overdue is retired, Finanzas 50, and no longer listed). Before a finished read: status not_read / reading / failed and null dimensions. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT and no glosa, on both sides; Chipax boletas carry neither RUT nor name. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read and finanzas_reports:read (the Contador seat, owners and admins).

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-10",
    "label": "octubre 2026",
    "status": "ready",
    "read": {
      "id": "01930000-0000-7000-8000-000000000017",
      "month": "2026-10",
      "status": "done",
      "history_from": "2025-01-01",
      "error": null,
      "requested_by": "00000000-0000-4000-8000-000000000001",
      "started_at": null,
      "finished_at": null,
      "created_at": "2026-11-02T12:00:00.000Z"
    },
    "active_read": null,
    "last_error": null,
    "history_from": "2025-01-01",
    "clean": false,
    "warnings": [],
    "difference_count": 1,
    "sections": [
      {
        "key": "balances",
        "difference_count": 0,
        "in_headline": true
      },
      {
        "key": "payables",
        "difference_count": 1,
        "in_headline": true
      },
      {
        "key": "costs",
        "difference_count": 0,
        "in_headline": true
      },
      {
        "key": "income",
        "difference_count": 2,
        "in_headline": true
      },
      {
        "key": "possible_duplicates",
        "difference_count": 2,
        "in_headline": false
      }
    ],
    "balances": {
      "matches": true,
      "difference_count": 0,
      "row_count": 1,
      "matching_count": 1,
      "item_difference_count": 0,
      "without_balance_count": 0,
      "rows": [
        {
          "key": "bank:01930000-0000-7000-8000-0000000000b1",
          "label": "Cuenta corriente",
          "chipax_clp": 12480300,
          "vitrina_clp": 12480300,
          "difference_clp": 0,
          "matches": true,
          "item_count": 212,
          "difference_count": 0,
          "basis": "closing",
          "vitrina_account": {
            "id": "01930000-0000-7000-8000-0000000000b1",
            "name": "Cuenta corriente",
            "institution_name": "Banco de Chile",
            "number_masked": "•••• 4321"
          },
          "chipax_account": {
            "external_id": "881",
            "name": "Cuenta corriente",
            "bank": "Banco de Chile",
            "number_masked": "•••• 4321"
          },
          "movements": {
            "chipax_in_clp": 30100000,
            "chipax_out_clp": 27620000,
            "vitrina_in_clp": 30100000,
            "vitrina_out_clp": 27620000
          }
        }
      ]
    },
    "payables": {
      "matches": false,
      "chipax_clp": 4210000,
      "vitrina_clp": 3800000,
      "difference_clp": 410000,
      "difference_count": 1,
      "row_count": 1,
      "matching_count": 0,
      "item_difference_count": 1,
      "rows": [
        {
          "key": "p_0000000000000001",
          "label": "Laboratorio Dental Andes Ltda",
          "chipax_clp": 410000,
          "vitrina_clp": null,
          "difference_clp": 410000,
          "matches": false,
          "item_count": 1,
          "difference_count": 1,
          "rut": "76086428-5",
          "contraparte_id": null
        }
      ]
    },
    "costs": {
      "matches": true,
      "chipax_clp": 9340000,
      "vitrina_clp": 9340000,
      "difference_clp": 0,
      "difference_count": 0,
      "row_count": 0,
      "matching_count": 0,
      "item_difference_count": 0,
      "rows": []
    },
    "income": {
      "difference_count": 2,
      "row_count": 6,
      "matching_count": 4,
      "bridge": {
        "vitrina_clp": 31500000,
        "chipax_clp": 30100000,
        "difference_clp": -1400000,
        "explained": true,
        "lines": [
          {
            "key": "timing_vitrina",
            "label": "Emitidos en el mes que Chipax no cuenta en el mes (los cuenta al cobrarlos)",
            "amount_clp": -2100000,
            "item_count": 14
          },
          {
            "key": "timing_chipax",
            "label": "Documentos de otros meses que Chipax cuenta en este mes",
            "amount_clp": 700000,
            "item_count": 5
          }
        ]
      },
      "rows": [],
      "production": null
    }
  }
}

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

GET /finanzas/chipax/shadow/{month}/export

Mes en paralelo: download the diff (xlsx)

One workbook: «Resumen», «Saldos bancarios», «Por pagar», «Costos y gastos», «Puente de ingresos» and «Diferencias» (every item that does not match, all dimensions). A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT and no glosa, on both sides; Chipax boletas carry neither RUT nor name. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read, finanzas_reports:read and finanzas:export. 404 before a finished read.

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow/<id>/export \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/chipax/shadow/{month}/items

Mes en paralelo: the items behind a row

The items of one row of the diff (key from the diff; bridge:&lt;line&gt; for an income bridge line), both sides side by side, biggest difference first. By default only the items that do not match. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT and no glosa, on both sides; Chipax boletas carry neither RUT nor name. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read and finanzas_reports:read (the Contador seat, owners and admins). 404 before a finished read.

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
dimensionquerybalances \payables \costs \
keyquerystringnomáx. 200
statusquerydifferences \allno
limitqueryintegerno≥ 1, ≤ 500
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow/<id>/items \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-10",
    "dimension": "payables",
    "key": "p_0000000000000001",
    "items": [
      {
        "key": "chipax:compra:9002",
        "row_key": "p_0000000000000001",
        "other_row_key": null,
        "status": "chipax_only",
        "chipax": {
          "type": "document",
          "id": null,
          "chipax_ref": "compra:9002",
          "label": "Factura electrónica N° 452",
          "dte_type": 33,
          "day": "2026-10-05",
          "amount_clp": 410000,
          "counterparty_name": "Laboratorio Dental Andes Ltda",
          "counterparty_rut": "76086428-5",
          "description": null
        },
        "vitrina": null,
        "vitrina_document_id": null,
        "difference_clp": 410000,
        "bridge_line": null
      }
    ],
    "total": 1,
    "limit": 50,
    "offset": 0,
    "difference_clp": 410000
  }
}

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

GET /finanzas/classifications

Classification markers for a page of documents or movements

For up to 200 ids of one type: classified or not, who classified it (source: manual, rule = marker «Regla», ia, chipax), the accounts and whether it is split. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
typequeryFinanzasItemTypesí
idsquerystringsímín. 1
curl https://api.vitrinadev.com/api/v1/finanzas/classifications \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "state": "classified",
        "source": "rule",
        "rule": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
          "name": "Jardinero"
        },
        "accounts": [
          {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
            "name": "Mantenimiento",
            "path": "Gastos de administración › Mantenimiento"
          }
        ],
        "split": false
      },
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
        "state": "unclassified",
        "source": null,
        "rule": null,
        "accounts": [],
        "split": false
      }
    ]
  }
}

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

DELETE /finanzas/classifications/{type}/{id}

Unclassify a document or movement

Removes the lines (back to «sin clasificar»); audited. Idempotent. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
typepathdocuments \movementssí
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/classifications/<id>/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "item": {
      "type": "document",
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a01",
      "amount_clp": 1190000,
      "day": "2026-10-05",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5d01",
        "name": "Laboratorio Dental Andes Ltda"
      },
      "counterparty_name": "LABORATORIO DENTAL ANDES LTDA",
      "description": null,
      "direction": null,
      "document_kind": "purchase",
      "expense_document_id": null
    },
    "state": "unclassified",
    "source": null,
    "rule": null,
    "lines": [],
    "history": []
  }
}

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

GET /finanzas/classifications/{type}/{id}

An item’s classification and its history

The lines of a document (documents) or bank movement (movements) and every classify / reclassify / unclassify (who, when, before, after). Classification is not double-entry: each line puts whole CLP on one account in one accrual month (YYYY-MM), and the lines of an item sum EXACTLY to its total (sum_mismatch otherwise; a remainder is never absorbed). A movement is never classified to an ingreso account (movement_to_income): income comes from documents. Rule breaks answer 422 FINANZAS_CLASSIFICATION_INVALID, state conflicts 409 FINANZAS_CLASSIFICATION_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
typepathdocuments \movementssí
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/classifications/<id>/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "item": {
      "type": "document",
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a01",
      "amount_clp": 1190000,
      "day": "2026-10-05",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5d01",
        "name": "Laboratorio Dental Andes Ltda"
      },
      "counterparty_name": "LABORATORIO DENTAL ANDES LTDA",
      "description": null,
      "direction": null,
      "document_kind": "purchase",
      "expense_document_id": null
    },
    "state": "classified",
    "source": "manual",
    "rule": null,
    "lines": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5e01",
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
          "name": "Laboratorio",
          "path": "Costos directos › Laboratorio",
          "kind": "costo",
          "archived": false
        },
        "amount_clp": 900000,
        "period": "2026-10",
        "source": "manual",
        "rule": null,
        "classified_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "created_at": "2026-10-30T15:20:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5e02",
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d11",
          "name": "Insumos clínicos",
          "path": "Costos directos › Insumos clínicos",
          "kind": "costo",
          "archived": false
        },
        "amount_clp": 290000,
        "period": "2026-09",
        "source": "manual",
        "rule": null,
        "classified_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "created_at": "2026-10-30T15:20:00.000Z"
      }
    ],
    "history": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6001",
        "action": "reclassify",
        "actor": "user",
        "actor_user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "rule_id": null,
        "before": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
            "amount_clp": 1190000,
            "period": "2026-10"
          }
        ],
        "after": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
            "amount_clp": 900000,
            "period": "2026-10"
          },
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d11",
            "amount_clp": 290000,
            "period": "2026-09"
          }
        ],
        "created_at": "2026-10-30T15:20:00.000Z"
      }
    ]
  }
}

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

PUT /finanzas/classifications/{type}/{id}

Classify, split or reclassify a document or movement

Replaces the item’s lines. One account for the whole amount (account_id / account_name, optional period), or lines to split it across accounts and accrual months («Dividir»). Reclassifying is allowed and audited. A movement a gasto was created from answers 409 expense_exists (classify the gasto); one with a live reconciliation link answers 409 movement_linked (undo the link first). Name the account by account_id or in plain language with account_name (name or path, no case/accents); an ambiguous name answers 422 account_ambiguous with details.candidates to choose from. Classification is not double-entry: each line puts whole CLP on one account in one accrual month (YYYY-MM), and the lines of an item sum EXACTLY to its total (sum_mismatch otherwise; a remainder is never absorbed). A movement is never classified to an ingreso account (movement_to_income): income comes from documents. Rule breaks answer 422 FINANZAS_CLASSIFICATION_INVALID, state conflicts 409 FINANZAS_CLASSIFICATION_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
typepathdocuments \movementssí
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X PUT https://api.vitrinadev.com/api/v1/finanzas/classifications/<id>/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      {
        "account_name": "Laboratorio",
        "amount_clp": 900000,
        "period": "2026-10"
      },
      {
        "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d11",
        "amount_clp": 290000,
        "period": "2026-09"
      }
    ]
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "item": {
      "type": "document",
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a01",
      "amount_clp": 1190000,
      "day": "2026-10-05",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5d01",
        "name": "Laboratorio Dental Andes Ltda"
      },
      "counterparty_name": "LABORATORIO DENTAL ANDES LTDA",
      "description": null,
      "direction": null,
      "document_kind": "purchase",
      "expense_document_id": null
    },
    "state": "classified",
    "source": "manual",
    "rule": null,
    "lines": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5e01",
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
          "name": "Laboratorio",
          "path": "Costos directos › Laboratorio",
          "kind": "costo",
          "archived": false
        },
        "amount_clp": 900000,
        "period": "2026-10",
        "source": "manual",
        "rule": null,
        "classified_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "created_at": "2026-10-30T15:20:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5e02",
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d11",
          "name": "Insumos clínicos",
          "path": "Costos directos › Insumos clínicos",
          "kind": "costo",
          "archived": false
        },
        "amount_clp": 290000,
        "period": "2026-09",
        "source": "manual",
        "rule": null,
        "classified_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "created_at": "2026-10-30T15:20:00.000Z"
      }
    ],
    "history": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6001",
        "action": "reclassify",
        "actor": "user",
        "actor_user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5f01",
        "rule_id": null,
        "before": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
            "amount_clp": 1190000,
            "period": "2026-10"
          }
        ],
        "after": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
            "amount_clp": 900000,
            "period": "2026-10"
          },
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d11",
            "amount_clp": 290000,
            "period": "2026-09"
          }
        ],
        "created_at": "2026-10-30T15:20:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/classifications/bulk

Classify many documents or movements to one account

Up to 500 items, each with its whole amount and its own month (or period for all). One transaction: all or nothing. Classified items are reclassified (audited); a reconciled movement refuses the batch (409 movement_linked). Name the account by account_id or in plain language with account_name (name or path, no case/accents); an ambiguous name answers 422 account_ambiguous with details.candidates to choose from. Classification is not double-entry: each line puts whole CLP on one account in one accrual month (YYYY-MM), and the lines of an item sum EXACTLY to its total (sum_mismatch otherwise; a remainder is never absorbed). A movement is never classified to an ingreso account (movement_to_income): income comes from documents. Rule breaks answer 422 FINANZAS_CLASSIFICATION_INVALID, state conflicts 409 FINANZAS_CLASSIFICATION_CONFLICT, both with details.reason. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/classifications/bulk \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01"
      },
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02"
      }
    ],
    "account_name": "Mantenimiento"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "classified": 2,
    "items": [
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01"
      },
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02"
      }
    ]
  }
}

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

GET /finanzas/connect-setup

What is connected and what comes next (bank, SII, honorarios)

«Conecta tus cuentas»: the bank (Fintoc links, Mercado Pago, or bank movements already arriving by e-mail/cartola), the SII connection of one sociedad with its four steps (razon_social, certificate, mandato, honorarios, the Clave Tributaria for the boletas de honorarios) and next_step, where the guided flow resumes, plus the first-visit onboarding mode (guided | checklist | done). Step states: todo, waiting (the SII or the representante legal is on it), done, problem (with reason), skipped (only honorarios, after «Mi empresa no recibe boletas de honorarios», never while honorarios documents are on record). Reads the stored SII state; refresh=1 reads the SII live. connection_id picks one sociedad (default: the first not yet complete). Connecting never turns on boleta emission. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
refreshquery0 \1no
connection_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "bank": {
      "state": "done",
      "connected": 1,
      "has_movements": true
    },
    "sii": {
      "connection": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
        "legal_name": "Clínica Ejemplo SpA",
        "rut": "76123456-7",
        "emission_on": false
      },
      "steps": [
        {
          "key": "razon_social",
          "state": "done",
          "reason": null
        },
        {
          "key": "certificate",
          "state": "done",
          "reason": null
        },
        {
          "key": "mandato",
          "state": "waiting",
          "reason": null
        },
        {
          "key": "honorarios",
          "state": "todo",
          "reason": null
        }
      ],
      "next_step": "honorarios",
      "complete": false,
      "honorarios": {
        "evidence": false,
        "declared_none_at": null,
        "can_skip": true
      },
      "sociedades": [
        {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
          "legal_name": "Clínica Ejemplo SpA",
          "tax_id": "76123456-7",
          "connected": true
        }
      ],
      "suggestion": null,
      "live": false
    },
    "onboarding": {
      "mode": "guided",
      "started_at": "2026-11-02T13:00:00.000Z",
      "deferred_at": null
    }
  }
}

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

POST /finanzas/connect-setup/onboarding

Onboarding state: start, defer, or «no recibe boletas de honorarios»

Per workspace. start keeps the guided start while things get connected; defer («Lo hago después») collapses it to the checklist on Resumen; no_honorarios marks the honorarios step skipped (409 while honorarios documents are on record); has_honorarios undoes it; dismiss_mercado_pago hides the optional Mercado Pago card for good (per workspace, every device). Answers the setup. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

Cuerpo

CampoTipoObligatorioRestricciones
actionstart \defer \no_honorarios \
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connect-setup/onboarding \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "defer"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "bank": {
      "state": "done",
      "connected": 1,
      "has_movements": true
    },
    "sii": {
      "connection": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
        "legal_name": "Clínica Ejemplo SpA",
        "rut": "76123456-7",
        "emission_on": false
      },
      "steps": [
        {
          "key": "razon_social",
          "state": "done",
          "reason": null
        },
        {
          "key": "certificate",
          "state": "done",
          "reason": null
        },
        {
          "key": "mandato",
          "state": "waiting",
          "reason": null
        },
        {
          "key": "honorarios",
          "state": "todo",
          "reason": null
        }
      ],
      "next_step": "honorarios",
      "complete": false,
      "honorarios": {
        "evidence": false,
        "declared_none_at": null,
        "can_skip": true
      },
      "sociedades": [
        {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
          "legal_name": "Clínica Ejemplo SpA",
          "tax_id": "76123456-7",
          "connected": true
        }
      ],
      "suggestion": null,
      "live": false
    },
    "onboarding": {
      "mode": "guided",
      "started_at": "2026-11-02T13:00:00.000Z",
      "deferred_at": null
    }
  }
}

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

POST /finanzas/connect-setup/sii

Connect a razón social to the SII (step 1)

Records the razón social when the workspace has none (legal_entity, the Datos legales body) or takes an existing one (legal_entity_id), then creates its SII connection. Idempotent per sociedad: an already connected one answers its connection. resolucion keeps an existing electronic-emission authorisation. Boleta emission stays OFF. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

Cuerpo

CampoTipoObligatorioRestricciones
legal_entity_iduuid—
legal_entityobject—
resolucionobject—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "connection_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
    "legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
    "created_legal_entity": false
  }
}

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

POST /finanzas/connect-setup/sii/{id}/mandato

Vitrina's mandato for the representante legal to sign (step 3)

Renders Vitrina's mandato (GET /legal/documents/\{key\} with key sii_mandato, version + SHA-256) for this razón social and opens it in Vitrina's signing service. Returns the representante legal's signing_url (also mailed to them unless send_email: false). A new request replaces an unsigned one; a signed mandato cannot be replaced. Once signed it is registered at the SII automatically (sii.mandato.phase: connecting → connected). Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
signerobject—
send_emailboolean—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii/<id>/mandato \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signer": {
      "name": "María Pérez Soto",
      "rut": "12.345.678-5",
      "email": "[email protected]"
    },
    "send_email": true
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "signing_url": "https://sign.example.invalid/sign/abc",
    "status": "pending",
    "signature": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b09",
      "phase": "pending_signature",
      "status": "pending_signature",
      "terms_version": "vitrina-mandato-1.0",
      "terms_sha256": "4398b7f80be1fd551c62325241e39c9e14138a63860f0cb4dcdc5be175f981b4",
      "signer": {
        "name": "María Pérez Soto",
        "rut": "12.345.678-5",
        "email": "[email protected]"
      },
      "signing_url": "https://sign.example.invalid/sign/abc",
      "signed_at": null,
      "attested_at": null,
      "problem": null,
      "document_available": false,
      "created_at": "2026-11-02T15:00:00.000Z"
    },
    "email_sent": true
  }
}

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

GET /finanzas/connect-setup/sii/{id}/mandato/document

The signed mandato PDF

The signed mandato of this razón social (file=signed, default) or the signing service's certificate (file=certificate). 404 until it is signed. Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
filequerysigned \certificateno
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii/<id>/mandato/document \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /finanzas/connect-setup/sii/{id}/mandato/revoke

Revoke the mandato (cláusula 7.1)

Revokes the signed mandato at the SII and here, with immediate effect: the SII connection stops acting for the empresa until a new mandato is signed. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii/<id>/mandato/revoke \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "bank": {
      "state": "done",
      "connected": 1,
      "has_movements": true
    },
    "sii": {
      "connection": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
        "legal_name": "Clínica Ejemplo SpA",
        "rut": "76123456-7",
        "emission_on": false
      },
      "steps": [
        {
          "key": "razon_social",
          "state": "done",
          "reason": null
        },
        {
          "key": "certificate",
          "state": "done",
          "reason": null
        },
        {
          "key": "mandato",
          "state": "waiting",
          "reason": null
        },
        {
          "key": "honorarios",
          "state": "todo",
          "reason": null
        }
      ],
      "next_step": "honorarios",
      "complete": false,
      "honorarios": {
        "evidence": false,
        "declared_none_at": null,
        "can_skip": true
      },
      "sociedades": [
        {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
          "legal_name": "Clínica Ejemplo SpA",
          "tax_id": "76123456-7",
          "connected": true
        }
      ],
      "suggestion": null,
      "live": false
    },
    "onboarding": {
      "mode": "guided",
      "started_at": "2026-11-02T13:00:00.000Z",
      "deferred_at": null
    }
  }
}

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

GET /finanzas/connect-setup/sociedad-lookup

The SII's public record of a RUT (razón social prefill)

A suggestion for the razón social form: legal name and a giro that fits the SII's 40 characters, from the SII's public nómina of personas jurídicas. null when the RUT is not there (a persona natural) or the lookup is unavailable; 400 for an invalid RUT. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
rutquerystringsímín. 3, máx. 20
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sociedad-lookup \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "source": "sii",
    "rut": "76123456-7",
    "legal_name": "CLINICA EJEMPLO SPA",
    "giro": "SERVICIOS ODONTOLOGICOS",
    "address_street": null,
    "comuna_code": null,
    "comuna_name": null
  }
}

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

GET /finanzas/connections

Money connections: banks, Mercado Pago and SII

Every money connection of the workspace (Ajustes › Bancos y SII): bank links, the Mercado Pago account and the SII connection per company, each with its accounts (type and currency), movement count, last sync, last successful sync and a status: ok, pending, stale (no successful sync for more than 48 h), error, login_required (the bank login must be redone: reconnect) or disconnected. next_refresh_at says when a manual sync is allowed again. fintoc.widget_public_key is the publishable key to open the bank-link widget with (null until configured). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/connections \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connections": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
        "kind": "bank",
        "provider": "fintoc",
        "institution_name": "Banco de Chile",
        "holder_name": "Clínica Ejemplo SpA",
        "accounts": [
          {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
            "number_masked": "****4821",
            "type": "cuenta_corriente",
            "currency": "CLP"
          }
        ],
        "movement_count": 1284,
        "connected_at": "2026-10-01T14:03:00.000Z",
        "last_sync_at": "2026-10-30T08:00:00.000Z",
        "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
        "last_sync_error": null,
        "status": "ok",
        "next_refresh_at": "2026-10-30T09:00:00.000Z",
        "actions": {
          "sync_now": true,
          "reconnect": false,
          "disconnect": true
        }
      }
    ],
    "fintoc": {
      "configured": true,
      "entitled": true,
      "feature": "bank_feed_fintoc",
      "price_clp_per_bank_month": 15000,
      "widget_public_key": "pk_test_ejemplo"
    },
    "stale_after_hours": 48,
    "manual_sync_min_interval_minutes": 60
  }
}

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

GET /finanzas/connections/{id}/clave-tributaria

SII clave status for boletas de honorarios

The state of the Clave Tributaria an SII connection uses to read boletas de honorarios: activa (with the last verified date), verificando, requiere_actualizar (the SII rejected the stored clave — changed or blocked — and the honorarios sync is paused until it is updated) or no_configurada. Also the consent text to show and whether it must be accepted again. Never returns the clave, not even masked. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/clave-tributaria \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "connection_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "status": "activa",
    "status_changed_at": "2026-10-30T12:00:04.000Z",
    "verified_at": "2026-10-30T12:00:04.000Z",
    "last_used_at": "2026-10-31T06:10:00.000Z",
    "pending_replacement": null,
    "last_verification": {
      "outcome": "verified",
      "at": "2026-10-30T12:00:04.000Z"
    },
    "consent": {
      "version": "vitrina-mandato-1.0#6.5",
      "accepted_at": "2026-10-30T12:00:00.000Z",
      "current": true
    },
    "consent_required": false,
    "consent_text": {
      "version": "vitrina-mandato-1.0#6.5",
      "title": "Conectar boletas de honorarios",
      "points": [
        "Guardamos la Clave Tributaria de la empresa para leer sus boletas de honorarios desde el SII."
      ],
      "authorization": "Autorizo a Vitrina a guardar la Clave Tributaria de la empresa y usarla solo para leer sus boletas de honorarios."
    },
    "live": true,
    "checked_at": "2026-10-31T06:10:00.000Z",
    "actions": {
      "connect": false,
      "update": true,
      "disconnect": true
    }
  }
}

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

POST /finanzas/connections/{id}/disconnect

Disconnect a bank account

Reversible: the link stays (disabled), its movements stay in the ledger, it stops syncing and stops counting as a billed bank. Bank links only. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/disconnect \
  -H "Authorization: Bearer $VITRINA_KEY"

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

POST /finanzas/connections/{id}/reconnect

Reconnect a bank account

For a bank link whose login must be redone (login_required). Send the exchange_token from Fintoc's widget opened again for that bank. The link returns to active; if Fintoc answers with a different link, it replaces this one, which is disconnected. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/reconnect \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "exchange_token": "exchange_token_ejemplo_123"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "kind": "bank",
    "provider": "fintoc",
    "institution_name": "Banco de Chile",
    "holder_name": "Clínica Ejemplo SpA",
    "accounts": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "number_masked": "****4821",
        "type": "cuenta_corriente",
        "currency": "CLP"
      }
    ],
    "movement_count": 1284,
    "connected_at": "2026-10-01T14:03:00.000Z",
    "last_sync_at": "2026-10-30T08:00:00.000Z",
    "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
    "last_sync_error": null,
    "status": "ok",
    "next_refresh_at": "2026-10-30T09:00:00.000Z",
    "actions": {
      "sync_now": true,
      "reconnect": false,
      "disconnect": true
    }
  }
}

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

POST /finanzas/connections/{id}/sync

Sync a connection now

«Sincronizar ahora». Queues a pull of a bank link or the Mercado Pago account. A bank link can be synced on demand at most once per manual_sync_min_interval_minutes; sooner answers 429 FINANZAS_SYNC_THROTTLED with details.next_refresh_at. A login_required link, a disconnected one or an SII connection answers 409. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/sync \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (202)

{
  "data": {
    "enqueued": true,
    "next_refresh_at": "2026-10-30T09:00:00.000Z"
  }
}

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

POST /finanzas/connections/fintoc

Connect a bank account

Send the exchange_token Fintoc's widget returns (or a link_token). The link token is stored encrypted and the first sync is queued. Each bank is billed at Fintoc's cost through the bank_feed_fintoc add-on; without it the answer is 402 ENTITLEMENT_NOT_ACTIVE (required: ["bank_feed_fintoc"]). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/fintoc \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "exchange_token": "exchange_token_ejemplo_123"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "kind": "bank",
    "provider": "fintoc",
    "institution_name": "Banco de Chile",
    "holder_name": "Clínica Ejemplo SpA",
    "accounts": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "number_masked": "****4821",
        "type": "cuenta_corriente",
        "currency": "CLP"
      }
    ],
    "movement_count": 1284,
    "connected_at": "2026-10-01T14:03:00.000Z",
    "last_sync_at": "2026-10-30T08:00:00.000Z",
    "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
    "last_sync_error": null,
    "status": "ok",
    "next_refresh_at": "2026-10-30T09:00:00.000Z",
    "actions": {
      "sync_now": true,
      "reconnect": false,
      "disconnect": true
    }
  }
}

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

POST /finanzas/connections/fintoc/link-intent

Start a bank link (Fintoc widget token)

Opens one bank-link attempt: creates a Fintoc link intent (business holder, movements) and returns its widget_token, which opens Fintoc's widget (Fintoc.create(\{ publicKey, widgetToken \})). Fintoc returns the token only once, so request a fresh one each time the widget opens. On success the widget hands back an exchange_token: send it to POST /finanzas/connections/fintoc (or …/\{id\}/reconnect). Reads no bank and costs nothing. Requires the bank_feed_fintoc add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/fintoc/link-intent \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (201)

{
  "data": {
    "widget_token": "widget_token_ejemplo_123",
    "external_id": "li_ejemplo123"
  }
}

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

GET /finanzas/connections/sync

Freshness of the bank movements

When each bank link was last pulled, and whether a pull is queued. A read: it queues nothing. Poll it after POST /finanzas/connections/sync until queued is false, then reload. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/connections/sync \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "last_sync_at": "2026-10-30T08:00:00.000Z",
    "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
    "queued": true,
    "enqueued": 0,
    "links": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
        "institution_name": "Banco de Chile",
        "last_sync_at": "2026-10-30T08:00:00.000Z",
        "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
        "queued": true,
        "next_refresh_at": "2026-10-30T09:15:00.000Z",
        "skipped": null
      }
    ],
    "min_interval_minutes": 15
  }
}

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

POST /finanzas/connections/sync

Sync every bank link

Sync on entry to Finanzas and «Sincronizar ahora» for every bank link. Idempotent, never throttled: each active link not pulled nor requested in the last min_interval_minutes (15) gets one pull, the others answer with their last pull (enqueued counts the pulls this call queued). A login_required or disconnected link is skipped and reported in links[].skipped. Movements land within a minute or two; poll GET /finanzas/connections/sync until queued is false. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/sync \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "last_sync_at": "2026-10-30T08:00:00.000Z",
    "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
    "queued": true,
    "enqueued": 1,
    "links": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
        "institution_name": "Banco de Chile",
        "last_sync_at": "2026-10-30T08:00:00.000Z",
        "last_successful_sync_at": "2026-10-30T08:00:00.000Z",
        "queued": true,
        "next_refresh_at": "2026-10-30T09:15:00.000Z",
        "skipped": null
      }
    ],
    "min_interval_minutes": 15
  }
}

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

GET /finanzas/contador-export

Download the Contador package for a month (zip)

One zip, paquete-contador-&lt;period&gt;.zip, with seven xlsx: 01-libro-de-compras, 02-libro-de-ventas, 03-honorarios, 04-movimientos-por-cuenta (one sheet per bank account), 05-clasificaciones-por-cuenta (net, as the Resultado counts), 06-resultado and 07-ajustes-posteriores-al-cierre, each &lt;name&gt;-&lt;period&gt;.xlsx, built by the same code as each screen and its export. Read-only. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read, finanzas_reports:read and finanzas:export (the Contador seat, owners and admins).

ParámetroEnTipoObligatorioRestricciones
periodquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/contador-export \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/contador-export/manifest

What the Contador package for a month holds

The same package as the download, without the bytes: the period, the zip name and each file (key, label, name, rows, size). Over MCP this is the read that generates and checks the package; the zip itself downloads over HTTPS. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» with no RUT; the libro de ventas keeps the patient RUT on facturas and on notas de crédito or débito that cite one, and boletas carry neither RUT nor name. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read, finanzas_reports:read and finanzas:export (the Contador seat, owners and admins).

ParámetroEnTipoObligatorioRestricciones
periodquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/contador-export/manifest \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "period": "2026-09",
    "filename": "paquete-contador-2026-09.zip",
    "files": [
      {
        "key": "libro_compras",
        "label": "Libro de compras",
        "filename": "01-libro-de-compras-2026-09.xlsx",
        "rows": 84,
        "size_bytes": 14210
      },
      {
        "key": "libro_ventas",
        "label": "Libro de ventas",
        "filename": "02-libro-de-ventas-2026-09.xlsx",
        "rows": 1312,
        "size_bytes": 61904
      },
      {
        "key": "honorarios",
        "label": "Honorarios",
        "filename": "03-honorarios-2026-09.xlsx",
        "rows": 12,
        "size_bytes": 7480
      },
      {
        "key": "movimientos",
        "label": "Movimientos por cuenta",
        "filename": "04-movimientos-por-cuenta-2026-09.xlsx",
        "rows": 640,
        "size_bytes": 38112
      },
      {
        "key": "clasificaciones",
        "label": "Clasificaciones por cuenta",
        "filename": "05-clasificaciones-por-cuenta-2026-09.xlsx",
        "rows": 702,
        "size_bytes": 40356
      },
      {
        "key": "resultado",
        "label": "Resultado",
        "filename": "06-resultado-2026-09.xlsx",
        "rows": 1398,
        "size_bytes": 52018
      },
      {
        "key": "ajustes",
        "label": "Ajustes posteriores al cierre",
        "filename": "07-ajustes-posteriores-al-cierre-2026-09.xlsx",
        "rows": 3,
        "size_bytes": 6920
      }
    ]
  }
}

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

GET /finanzas/contrapartes

List Contrapartes (suppliers and customers)

The Contrapartes directory: every business or person the workspace has money with, created automatically from bank movements and documents. Keyed by RUT, or by name when there is none (e.g. «Meta Ads»). q matches the name or a RUT in any punctuation (also without its check digit); role filters supplier (proveedor), customer (cliente) or both. Sorted by name; merged-away ones are not listed. Each row carries its movement and document counts. Offset-paginated: meta.pagination.total counts every match. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
qquerystringnomáx. 200
rolequerysupplier \customer \both
sortqueryname \activityno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "name": "Insumos Médicos Ejemplo SpA",
      "rut": "76123456-7",
      "rut_formatted": "76.123.456-7",
      "roles": [
        "supplier"
      ],
      "source": "movement",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 14,
      "document_count": 6,
      "merged_into_id": null,
      "payment_terms_days": null,
      "needs_name": false,
      "payment_terms_source": null,
      "created_at": "2026-10-01T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

GET /finanzas/contrapartes/{id}

Get a Contraparte

One Contraparte with its roles, its optional links (a Contact, a paciente) and its movement and document counts. A merged-away id still answers, with merged_into_id set to the one that remained. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "name": "Insumos Médicos Ejemplo SpA",
    "rut": "76123456-7",
    "rut_formatted": "76.123.456-7",
    "roles": [
      "supplier"
    ],
    "source": "movement",
    "contact": null,
    "clinic_patient": null,
    "is_payroll": false,
    "movement_count": 14,
    "document_count": 6,
    "merged_into_id": null,
    "payment_terms_days": null,
    "needs_name": false,
    "payment_terms_source": null,
    "created_at": "2026-10-01T14:03:00.000Z",
    "updated_at": "2026-10-30T08:00:00.000Z"
  }
}

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

PATCH /finanzas/contrapartes/{id}

Rename a Contraparte, or link or unlink it

Send only what changes: name renames it (the RUT and the matching key never change, so the next sync still finds it); contact_id / clinic_patient_id link it to a Contact or a paciente of this workspace, null unlinks (nothing on the Contact or paciente changes). A merged contact or paciente resolves to the one that remained. A merged-away Contraparte answers 409. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Insumos Médicos Ejemplo Ltda."
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "name": "Insumos Médicos Ejemplo Ltda.",
    "rut": "76123456-7",
    "rut_formatted": "76.123.456-7",
    "roles": [
      "supplier"
    ],
    "source": "movement",
    "contact": null,
    "clinic_patient": null,
    "is_payroll": false,
    "movement_count": 14,
    "document_count": 6,
    "merged_into_id": null,
    "payment_terms_days": null,
    "needs_name": false,
    "payment_terms_source": null,
    "created_at": "2026-10-01T14:03:00.000Z",
    "updated_at": "2026-10-30T08:00:00.000Z"
  }
}

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

POST /finanzas/contrapartes/{id}/aliases

Link a RUT or a bank account to a Contraparte

Linked RUTs and bank accounts: a company Contraparte (RUT 78…) can own a person's account (RUT 16…) it is paid into. The reconciliation engine treats a movement to or from a linked RUT or account as the owning Contraparte's, so that payment matches the company's factura. A RUT or an account belongs to ONE Contraparte per workspace. kind: rut takes a RUT in any punctuation (check digit validated; its own RUT answers 400); kind: bank_account takes the account number (dashes and leading zeros ignored, 6–30 digits). Already linked here or to another Contraparte → 409 (the message names it). Right after, the reconciliation engine runs over the last 60 days; reconciled says what it linked. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/aliases \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "rut",
    "value": "16.123.456-7"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "alias": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b01",
      "kind": "rut",
      "display": "16.123.456-7",
      "created_at": "2026-10-30T12:00:00.000Z"
    },
    "aliases": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b01",
        "kind": "rut",
        "display": "16.123.456-7",
        "created_at": "2026-10-30T12:00:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b02",
        "kind": "bank_account",
        "display": "•••• 5678",
        "created_at": "2026-10-30T12:01:00.000Z"
      }
    ],
    "reconciled": {
      "documents": 1,
      "transfers": 0,
      "cash_deposits": 0,
      "payments": 0,
      "expenses": 0
    }
  }
}

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

DELETE /finanzas/contrapartes/{id}/aliases/{aliasId}

Unlink a RUT, bank account or descriptor from a Contraparte

Linked RUTs and bank accounts: a company Contraparte (RUT 78…) can own a person's account (RUT 16…) it is paid into. The reconciliation engine treats a movement to or from a linked RUT or account as the owning Contraparte's, so that payment matches the company's factura. A RUT or an account belongs to ONE Contraparte per workspace. Unlinking stops new movements from matching through it; links already made stay (undo each one with «Deshacer»). Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
aliasIdpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/aliases/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "removed": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b01",
      "kind": "rut",
      "display": "16.123.456-7",
      "created_at": "2026-10-30T12:00:00.000Z"
    },
    "aliases": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b02",
        "kind": "bank_account",
        "display": "•••• 5678",
        "created_at": "2026-10-30T12:01:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/contrapartes/{id}/merge

Merge a duplicate Contraparte into this one

Merges merge_id into \{id\}: every movement, document and link moves to the survivor, which keeps its name and links and takes the other's links only where it has none; roles combine. When only merge_id has a RUT, that one is kept instead (survivor.id says which). Two different RUTs answer 409. Undoable: merge_id goes to POST /finanzas/contrapartes/merges/\{mergeId\}/undo; audited. The merged one stays as a tombstone so the next sync resolves to the survivor and never recreates it. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/merge \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merge_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "survivor": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "name": "Insumos Médicos Ejemplo SpA",
      "rut": "76123456-7",
      "rut_formatted": "76.123.456-7",
      "roles": [
        "supplier"
      ],
      "source": "movement",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 14,
      "document_count": 6,
      "merged_into_id": null,
      "payment_terms_days": null,
      "needs_name": false,
      "payment_terms_source": null,
      "created_at": "2026-10-01T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    },
    "merged_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
    "merge_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4aaa",
    "moved": {
      "movements": 3,
      "documents": 2
    }
  }
}

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

GET /finanzas/contrapartes/{id}/overview

The Contraparte page: balance, terms, linked accounts, rules

Everything on a Contraparte page except its timeline. balance.payable = what the workspace owes it (open purchases, honorarios, gastos); balance.receivable = what it owes the workspace (open sales). An open document counts its total minus its live reconciliation links; voided ones, ones no longer at the SII, notas de crédito and taxes are left out. Overdue = due before as_of (today, America/Santiago). terms gives its own terms and the effective terms per class it has documents in. A document is due its issue date plus the payment terms in days: the Contraparte's own terms, else the workspace default of its class (suppliers = compras, customers = ventas, honorarios, expenses = gastos; built-in 30 / 30 / 0 / 0). Changing terms recomputes the due date of every open document it covers; a paid one keeps its due date, and so does one whose source printed a due date. Taxes and notas de crédito have none. aliases are its linked RUTs and bank accounts (accounts masked) and the card / automatic-debit descriptors learned from «Por resolver» (kind: descriptor). rules are the active and paused rules that name it. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/overview \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "contraparte": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6a01",
      "name": "Laboratorio Dental Andes Ltda",
      "rut": "78123456-5",
      "rut_formatted": "78.123.456-5",
      "roles": [
        "supplier"
      ],
      "source": "document",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 9,
      "document_count": 12,
      "merged_into_id": null,
      "payment_terms_days": 45,
      "payment_terms_source": "manual",
      "created_at": "2026-09-02T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    },
    "balance": {
      "payable": {
        "open_clp": 2380000,
        "overdue_clp": 1190000,
        "open_count": 2,
        "overdue_count": 1,
        "oldest_overdue_due_date": "2026-10-20",
        "next_due_date": "2026-11-19"
      },
      "receivable": {
        "open_clp": 0,
        "overdue_clp": 0,
        "open_count": 0,
        "overdue_count": 0,
        "oldest_overdue_due_date": null,
        "next_due_date": null
      }
    },
    "terms": {
      "days": 45,
      "by_class": [
        {
          "class": "suppliers",
          "label": "Proveedores",
          "days": 45,
          "source": "contraparte",
          "origin": "manual"
        }
      ]
    },
    "aliases": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b01",
        "kind": "rut",
        "display": "16.123.456-7",
        "created_at": "2026-10-30T12:00:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6b02",
        "kind": "bank_account",
        "display": "•••• 5678",
        "created_at": "2026-10-30T12:01:00.000Z"
      }
    ],
    "rules": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6c01",
        "name": null,
        "label": "Documentos de Laboratorio Dental Andes Ltda → Costos directos › Laboratorio",
        "target": "document",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6a01",
          "name": "Laboratorio Dental Andes Ltda",
          "rut": "78.123.456-5"
        },
        "text_contains": null,
        "amount_min_clp": null,
        "amount_max_clp": null,
        "direction": null,
        "document_kind": null,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
          "name": "Laboratorio",
          "path": "Costos directos › Laboratorio",
          "kind": "costo",
          "archived": false
        },
        "status": "active",
        "run_count": 31,
        "last_applied_at": "2026-10-29T08:00:00.000Z",
        "created_at": "2026-09-01T12:00:00.000Z",
        "updated_at": "2026-10-29T08:00:00.000Z"
      }
    ],
    "open_case": {
      "id": "0190a3c2-bf70-7182-d394-0f1a2b3c4d5e"
    },
    "as_of": "2026-10-30"
  }
}

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

PUT /finanzas/contrapartes/{id}/payment-terms

Set a Contraparte's payment terms

days (0–365) overrides the workspace default of every class for this Contraparte; null goes back to the defaults. A document is due its issue date plus the payment terms in days: the Contraparte's own terms, else the workspace default of its class (suppliers = compras, customers = ventas, honorarios, expenses = gastos; built-in 30 / 30 / 0 / 0). Changing terms recomputes the due date of every open document it covers; a paid one keeps its due date, and so does one whose source printed a due date. Taxes and notas de crédito have none. Answers the new terms and how many documents got a new due date. A merged-away Contraparte answers 409. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PUT https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/payment-terms \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "days": 45
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "terms": {
      "days": 45,
      "by_class": [
        {
          "class": "suppliers",
          "label": "Proveedores",
          "days": 45,
          "source": "contraparte",
          "origin": "manual"
        }
      ]
    },
    "recomputed_documents": 3
  }
}

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

GET /finanzas/contrapartes/{id}/timeline

A Contraparte's movements and documents, newest first

Its documents and its movements — those that name it, and those a live reconciliation link ties to one of its documents or to it as a third party — ordered by date, newest first. A movement item carries the Movimientos object (its state, assigned_clp, pending_clp); a document item carries the Documentos object plus paid_clp, outstanding_clp and overdue. type keeps only one kind. Offset-paginated (meta.pagination.total). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
typequerydocument \movementno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/timeline \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "type": "movement",
      "date": "2026-10-28",
      "movement": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6e01",
        "date": "2026-10-28",
        "direction": "outbound",
        "amount_clp": 700000,
        "currency": "CLP",
        "original": null,
        "description": "TRANSF A JUAN PEREZ",
        "counterparty_name": "Juan Pérez",
        "counterparty_rut_formatted": "16.123.456-7",
        "contraparte": null,
        "bank_account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
          "name": "Cuenta corriente",
          "institution_name": "Banco de Chile",
          "number_masked": "•••• 4821",
          "currency": "CLP"
        },
        "state": "reconciled",
        "assigned_clp": 700000,
        "pending_clp": 0,
        "resolved_by": "match",
        "source": "bank_feed",
        "operation_id": null,
        "payer": null,
        "comment_count": 0,
        "ads_document_id": null,
        "card_processor": null,
        "generic_label": null,
        "created_at": "2026-10-28T09:00:00.000Z"
      }
    },
    {
      "type": "document",
      "date": "2026-10-05",
      "document": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6d01",
        "kind": "purchase",
        "source": "sii",
        "dte_type": 33,
        "dte_type_label": "Factura electrónica",
        "is_credit_note": false,
        "folio": "4521",
        "issue_date": "2026-10-05",
        "period": "2026-10",
        "received_on": "2026-10-06",
        "issuer_rut_formatted": "78.123.456-5",
        "receiver_rut_formatted": "76.543.219-4",
        "counterparty_name": "LABORATORIO DENTAL ANDES LTDA",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6a01",
          "name": "Laboratorio Dental Andes Ltda",
          "rut_formatted": "78.123.456-5"
        },
        "amounts": {
          "net": 1000000,
          "exempt": 0,
          "iva": 190000,
          "other_taxes": 0,
          "total": 1190000
        },
        "due_date": "2026-11-19",
        "payment_state": "parcial",
        "sii_estado_contable": "REGISTRO",
        "missing_at_sii": false,
        "voided": false,
        "adjusts": null,
        "created_at": "2026-10-06T05:31:12.000Z",
        "updated_at": "2026-10-28T05:31:12.000Z"
      },
      "paid_clp": 700000,
      "outstanding_clp": 490000,
      "overdue": false
    }
  ],
  "meta": {
    "pagination": {
      "total": 21,
      "limit": 50,
      "offset": 0
    }
  }
}

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

GET /finanzas/contrapartes/duplicates

Proposed Contraparte duplicates («Unir duplicados»)

Pairs that look like one business or person: similar names where at least one has no RUT (same normalised name with a known RUT is merged automatically and appears in Hecho por Vitrina instead). keep is the one that stays (the one with a RUT, else the more active), merge is merged into it, moves says how many movements and documents move. Pairs already rejected (dismiss) or undone are not proposed. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/duplicates \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "total": 1,
    "items": [
      {
        "keep": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
          "name": "ADOLFO CERDA ALVAREZ",
          "rut": "14432869-8",
          "rut_formatted": "14.432.869-8",
          "movement_count": 1,
          "document_count": 13
        },
        "merge": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
          "name": "Adolfo Cerda A",
          "rut": null,
          "rut_formatted": null,
          "movement_count": 1,
          "document_count": 0
        },
        "score": 0.9,
        "moves": {
          "movements": 1,
          "documents": 0
        }
      }
    ]
  }
}

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

POST /finanzas/contrapartes/duplicates/dismiss

Reject a proposed duplicate («No son la misma»)

The two are different: the pair is never proposed again and never merged automatically. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/duplicates/dismiss \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "a_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "b_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "a_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "b_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
    "dismissed": true
  }
}

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

POST /finanzas/contrapartes/duplicates/merge

Accept a proposed duplicate: merge it (undoable)

Same as POST /finanzas/contrapartes/\{id\}/merge (keep_id = the path id) but recorded as a review merge. Reversible: it shows in Hecho por Vitrina with «Deshacer» and merge_id can be passed to POST /finanzas/contrapartes/merges/\{mergeId\}/undo. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/duplicates/merge \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keep_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "merge_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "survivor": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "name": "Insumos Médicos Ejemplo SpA",
      "rut": "76123456-7",
      "rut_formatted": "76.123.456-7",
      "roles": [
        "supplier"
      ],
      "source": "movement",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 14,
      "document_count": 6,
      "merged_into_id": null,
      "payment_terms_days": null,
      "needs_name": false,
      "payment_terms_source": null,
      "created_at": "2026-10-01T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    },
    "merged_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
    "merge_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4aaa",
    "moved": {
      "movements": 1,
      "documents": 0
    }
  }
}

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

GET /finanzas/contrapartes/merges

Contrapartes merged in a month («Contrapartes unidas»)

Every merge of the month (Santiago), automatic or by a person, newest first, with undoable and undone_at. The same list rides on GET /finanzas/automatic-actions as contraparte_merges. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthquerystringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/merges \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4aaa",
      "origin": "same_name",
      "actor": "system",
      "label": "Alldental → ALLDENTAL SPA",
      "survivor": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
        "name": "ALLDENTAL SPA"
      },
      "merged": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
        "name": "Alldental"
      },
      "moved": {
        "movements": 0,
        "documents": 1
      },
      "reason": "Mismo nombre y un RUT conocido",
      "created_at": "2026-11-07T14:03:00.000Z",
      "undone_at": null,
      "undoable": true
    }
  ]
}

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

POST /finanzas/contrapartes/merges/{mergeId}/undo

Undo a Contraparte merge («Deshacer»)

Puts back on the original Contraparte exactly the movements, documents, rules and aliases the merge moved (not what a person moved since), restores its links, and makes it live again. The pair is not merged automatically again. 409 when already undone or when the survivor was merged away since (undo that one first). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
mergeIdpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/merges/<id>/undo \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "merge_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4aaa",
    "survivor": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "name": "Insumos Médicos Ejemplo SpA",
      "rut": "76123456-7",
      "rut_formatted": "76.123.456-7",
      "roles": [
        "supplier"
      ],
      "source": "movement",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 14,
      "document_count": 6,
      "merged_into_id": null,
      "payment_terms_days": null,
      "needs_name": false,
      "payment_terms_source": null,
      "created_at": "2026-10-01T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    },
    "restored": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "name": "Alldental",
      "rut": "76123456-7",
      "rut_formatted": "76.123.456-7",
      "roles": [
        "supplier"
      ],
      "source": "movement",
      "contact": null,
      "clinic_patient": null,
      "is_payroll": false,
      "movement_count": 14,
      "document_count": 6,
      "merged_into_id": null,
      "payment_terms_days": null,
      "needs_name": false,
      "payment_terms_source": null,
      "created_at": "2026-10-01T14:03:00.000Z",
      "updated_at": "2026-10-30T08:00:00.000Z"
    },
    "moved_back": {
      "movements": 0,
      "documents": 1
    }
  }
}

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

GET /finanzas/costos

Costos y equilibrio: fixed vs variable costs and the break-even

Reportes › Costos y equilibrio, from the classified result (the same net amounts as the Resultado). Per month and for the period: ingresos, costos variables (accounts flagged variable, documented or not, plus «Egresos sin clasificar», reported apart in sin_clasificar_clp), costos fijos, margen de contribución and its %, and the punto de equilibrio = fijos ÷ margen de contribución %, the monthly income at which the result is zero, with income against it (brecha_clp, cobertura_pct). An account without its own flag takes its nearest ancestor’s. Clinics also get dentist_share (the treating dentist’s share, a variable cost, with the optional reference line, «Un tercio de la venta» by default) and hora_sillon: fijos ÷ available chair-hours (chairs × opening hours of each branch; the current month counts the days lived so far), all costs ÷ attended hours (completed citas), occupancy and production per attended hour, per branch and total. Other verticals see break-even only. Default: January of the current year to the current month; at most 24 months. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/costos \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "from": "2026-09",
    "to": "2026-10",
    "current_month": "2026-10",
    "currency": "CLP",
    "is_clinic": true,
    "reference_month": "2026-09",
    "months": [
      {
        "month": "2026-09",
        "label": "septiembre 2026",
        "proforma": false,
        "ingresos_clp": 48000000,
        "variables_clp": 24000000,
        "sin_clasificar_clp": 0,
        "fijos_clp": 26000000,
        "contribucion_clp": 24000000,
        "contribucion_pct": 50,
        "equilibrio_clp": 52000000,
        "equilibrio_sin_calculo": null,
        "brecha_clp": -4000000,
        "cobertura_pct": 92.3,
        "resultado_clp": -2000000
      },
      {
        "month": "2026-10",
        "label": "octubre 2026",
        "proforma": true,
        "ingresos_clp": 30000000,
        "variables_clp": 14000000,
        "sin_clasificar_clp": 0,
        "fijos_clp": 25000000,
        "contribucion_clp": 16000000,
        "contribucion_pct": 53.3,
        "equilibrio_clp": 46875000,
        "equilibrio_sin_calculo": null,
        "brecha_clp": -16875000,
        "cobertura_pct": 64,
        "resultado_clp": -9000000
      }
    ],
    "period": {
      "ingresos_clp": 39000000,
      "variables_clp": 19000000,
      "sin_clasificar_clp": 0,
      "fijos_clp": 25500000,
      "contribucion_clp": 20000000,
      "contribucion_pct": 51.3,
      "equilibrio_clp": 49725000,
      "equilibrio_sin_calculo": null,
      "brecha_clp": -10725000,
      "cobertura_pct": 78.4,
      "resultado_clp": -5500000,
      "months": 2
    },
    "accounts": [
      {
        "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6101",
        "name": "Dentistas (honorarios y liquidaciones)",
        "path": "Costos de explotación › Dentistas (honorarios y liquidaciones)",
        "kind": "costo",
        "behavior": "variable",
        "behavior_inherited": false,
        "values": [
          16000000,
          9500000
        ],
        "total_clp": 25500000
      },
      {
        "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6102",
        "name": "Arriendo",
        "path": "Gastos de administración y venta › Gastos generales › Arriendo",
        "kind": "gasto",
        "behavior": "fijo",
        "behavior_inherited": false,
        "values": [
          4200000,
          4200000
        ],
        "total_clp": 8400000
      }
    ],
    "dentist_share": {
      "account_ids": [
        "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6101"
      ],
      "source": "detected",
      "values": [
        16000000,
        9500000
      ],
      "total_clp": 25500000,
      "pct": [
        33.3,
        31.7
      ],
      "pct_total": 32.7,
      "reference": {
        "enabled": true,
        "share_bps": 3333,
        "label": "Un tercio de la venta",
        "values": [
          15998400,
          9999000
        ],
        "total_clp": 25997400
      }
    },
    "hora_sillon": {
      "chairs": 6,
      "chairs_source": {
        "count": 6,
        "source": "resources",
        "source_label": "de tu agenda"
      },
      "lunch_break_minutes": 60,
      "available_hours": [
        1440,
        1056
      ],
      "attended_hours": [
        1087,
        780.5
      ],
      "available_hours_total": 2496,
      "attended_hours_total": 1867.5,
      "production_clp": [
        52000000,
        33000000
      ],
      "production_total_clp": 85000000,
      "production_source": "clinic_mirror",
      "occupancy_pct": [
        75.5,
        73.9
      ],
      "occupancy_pct_total": 74.8,
      "costo_hora_disponible_clp": [
        18056,
        23674
      ],
      "costo_hora_disponible_total_clp": 20433,
      "costo_hora_atendida_clp": [
        45998,
        50000
      ],
      "costo_hora_atendida_total_clp": 47657,
      "produccion_hora_atendida_clp": [
        47838,
        42281
      ],
      "produccion_hora_atendida_total_clp": 45515,
      "branches": [
        {
          "branch_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6201",
          "name": "Casa matriz",
          "chairs": 6,
          "available_hours": [
            1440,
            1056
          ],
          "attended_hours": [
            1087,
            780.5
          ],
          "available_hours_total": 2496,
          "attended_hours_total": 1867.5,
          "occupancy_pct": 74.8
        }
      ],
      "missing": []
    }
  }
}

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

GET /finanzas/costos/settings

Read how Costos y equilibrio reads the dentist share

Which accounts are the treating dentist’s share (null = detected: the clinic chart’s «Dentistas» account, or accounts named «Dentista…», «Profesionales…» or «Honorarios clínicos…»), and the optional reference line (on by default at 3333 bps, a third of the sale). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

curl https://api.vitrinadev.com/api/v1/finanzas/costos/settings \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "reference_share_enabled": true,
    "reference_share_bps": 3333,
    "professional_account_ids": null,
    "effective_professional_account_ids": [
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6101"
    ],
    "chairs_override": null,
    "lunch_break_minutes": null,
    "updated_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6301",
    "updated_at": "2026-10-31T12:00:00.000Z"
  }
}

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

PATCH /finanzas/costos/settings

Change the dentist share accounts or the reference line

Send only what changes. professional_account_ids: costo or gasto accounts of the plan de cuentas (null goes back to detection); reference_share_enabled toggles the line; reference_share_bps is its share of income (1–10000). chairs_override (1–200) sets the clinic’s chair count, shown as «elegido por ti»; null goes back to the clinic’s resources. lunch_break_minutes (0–240; null = 60) is the weekday break taken off the available hours. 400 VALIDATION_ERROR with details.reason account_not_found or account_not_cost. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Cuerpo

CampoTipoObligatorioRestricciones
reference_share_enabledboolean—
reference_share_bpsinteger—≥ 1, ≤ 10000
professional_account_idsarray | null—
chairs_overrideinteger | null—≥ 1, ≤ 200
lunch_break_minutesinteger | null—≥ 0, ≤ 240
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/costos/settings \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_share_enabled": false
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "reference_share_enabled": true,
    "reference_share_bps": 3333,
    "professional_account_ids": null,
    "effective_professional_account_ids": [
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6101"
    ],
    "chairs_override": null,
    "lunch_break_minutes": null,
    "updated_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6301",
    "updated_at": "2026-10-31T12:00:00.000Z"
  }
}

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

GET /finanzas/digest/preview

Preview the weekly Finanzas digest

What this week's «Resumen semanal de Finanzas» says to the caller, read now from the Resumen (GET /finanzas/resumen), so the figures match the home. The digest goes out every Monday from 09:00 workspace time, at most once per workspace and week, to every member with finanzas:read: email by default, WhatsApp only on the opt-in finanzas_whatsapp rail; each member turns it off in their notification preferences (finanzas.weekly_digest). A workspace with no finance data gets none (will_send: false). Without finanzas_reports:read there are no cash or result figures; without the patient roster patients read as initials. Sends nothing. Requires the finanzas add-on (402 ENTITLEMENT_NOT_ACTIVE otherwise) and finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/digest/preview \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "week_start": "2026-11-02",
    "send_at": "2026-11-02T12:00:00.000Z",
    "next_send_at": "2026-11-09T12:00:00.000Z",
    "status": "pending",
    "sent_at": null,
    "will_send": true,
    "channels": {
      "email": true,
      "whatsapp": false
    },
    "email": {
      "subject": "Resumen semanal de Finanzas: $12.340.000 en caja",
      "hero": {
        "label": "Caja hoy",
        "value": "$12.340.000",
        "caption": "A fin de mes: $8.200.000. En 30 días: $9.100.000."
      },
      "problem": {
        "text": "La caja alcanza hasta el 23 de noviembre. Ese martes pagas Arriendo y quedarías en negativo por $450.000.",
        "link": {
          "label": "Ver flujo de caja y cómo cubrirlo",
          "path": "/finanzas/reportes/flujo-de-caja"
        }
      },
      "lines": [
        "Noviembre va en $6.100.000 de ingresos, de $9.800.000 para cubrir costos.",
        "3 casos te necesitan en Por resolver, por $840.000."
      ],
      "primary": {
        "label": "Ver Resumen",
        "path": "/finanzas"
      },
      "secondary": {
        "label": "Ir a Por resolver",
        "path": "/finanzas/por-resolver"
      }
    },
    "whatsapp_text": "Caja hoy: $12.340.000. La caja alcanza hasta el 23 de noviembre. Ese martes pagas Arriendo y quedarías en negativo por $450.000. 3 casos te necesitan en Por resolver, por $840.000.",
    "figures": {
      "reports_visible": true,
      "cash_today_clp": 12340000,
      "month_end_clp": 8200000,
      "in_30_days_clp": 9100000,
      "runs_out_date": "2026-11-24",
      "ingresos_clp": 6100000,
      "resultado_clp": -1200000,
      "equilibrio_clp": 9800000,
      "open_cases": 3
    }
  }
}

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

GET /finanzas/documents

List documents (compras, ventas, honorarios, gastos, F29)

The Documentos register: every document the workspace has money on, one list filtered by kind — purchase (Compras, from the SII registro de compras), sale (Ventas: the SII registro de ventas and what was emitted through Vitrina), honorario, manual_expense, tax (F29). Each carries its Contraparte, DTE type, folio, issue date, net / IVA / exempt / total in whole CLP, due date and payment state; a nota de crédito or débito carries the document it adjusts. Filter by period (YYYY-MM) or from/to, Contraparte, payment state, DTE type, source, or q (a folio, a name or a RUT); paid_before_vitrina=true is the «Heredado» filter (Finanzas 50). Newest first, offset-paginated; meta.counts_by_kind counts the same filters per kind. The SII documents sync daily in the background. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
kindquerypurchase \sale \honorario \
sourcequerysii \vitrina \manual \
originqueryadsno
periodquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
contraparte_idqueryuuidno
payment_statequerypor_pagar \parcial \pagado \
paid_before_vitrinaquerytrue \falseno
dte_typequeryintegerno≥ 1, ≤ 999
qquerystringnomáx. 200
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/documents \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "kind": "purchase",
      "source": "sii",
      "dte_type": 61,
      "dte_type_label": "Nota de crédito electrónica",
      "is_credit_note": true,
      "folio": "45",
      "issue_date": "2026-10-10",
      "period": "2026-10",
      "received_on": "2026-10-11",
      "issuer_rut_formatted": "76.086.428-5",
      "receiver_rut_formatted": "76.543.219-4",
      "counterparty_name": "Laboratorio Andes SpA",
      "contraparte": {
        "id": "0190a3c2-6a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "name": "Laboratorio Andes SpA",
        "rut_formatted": "76.086.428-5"
      },
      "amounts": {
        "net": 10000,
        "exempt": 0,
        "iva": 1900,
        "other_taxes": 0,
        "total": 11900
      },
      "due_date": null,
      "payment_state": "por_pagar",
      "settlement": null,
      "sii_estado_contable": "REGISTRO",
      "missing_at_sii": false,
      "voided": false,
      "adjusts": {
        "id": "0190a3c2-7b3c-7d4e-9f50-6b7c8d9e0f1a",
        "dte_type": 33,
        "dte_type_label": "Factura electrónica",
        "folio": "1001"
      },
      "f29": null,
      "honorario": null,
      "created_at": "2026-10-30T05:31:12.000Z",
      "updated_at": "2026-10-30T05:31:12.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    },
    "counts_by_kind": {
      "purchase": 1,
      "sale": 0,
      "honorario": 0,
      "manual_expense": 0,
      "tax": 0
    }
  }
}

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

GET /finanzas/documents/{id}

Get a document

One document with its Contraparte, amounts, payment state, the document it adjusts (a nota de crédito / débito) and the notes that adjust it (adjusted_by). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/documents/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
    "kind": "purchase",
    "source": "sii",
    "dte_type": 61,
    "dte_type_label": "Nota de crédito electrónica",
    "is_credit_note": true,
    "folio": "45",
    "issue_date": "2026-10-10",
    "period": "2026-10",
    "received_on": "2026-10-11",
    "issuer_rut_formatted": "76.086.428-5",
    "receiver_rut_formatted": "76.543.219-4",
    "counterparty_name": "Laboratorio Andes SpA",
    "contraparte": {
      "id": "0190a3c2-6a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "name": "Laboratorio Andes SpA",
      "rut_formatted": "76.086.428-5"
    },
    "amounts": {
      "net": 10000,
      "exempt": 0,
      "iva": 1900,
      "other_taxes": 0,
      "total": 11900
    },
    "due_date": null,
    "payment_state": "por_pagar",
    "settlement": null,
    "sii_estado_contable": "REGISTRO",
    "missing_at_sii": false,
    "voided": false,
    "adjusts": {
      "id": "0190a3c2-7b3c-7d4e-9f50-6b7c8d9e0f1a",
      "dte_type": 33,
      "dte_type_label": "Factura electrónica",
      "folio": "1001"
    },
    "f29": null,
    "honorario": null,
    "created_at": "2026-10-30T05:31:12.000Z",
    "updated_at": "2026-10-30T05:31:12.000Z",
    "adjusted_by": [],
    "description": null,
    "files": []
  }
}

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

PATCH /finanzas/documents/{id}

Edit a manual document

Corrects a document uploaded or forwarded to Vitrina (source manual): amount, date, issuer RUT / name, Contraparte, type, folio, description. Audited with before / after. SII documents are the SII's word: 409 FINANZAS_DOCUMENT_CONFLICT not_manual. The amount cannot change while a reconciliation link or a classification holds it (amount_in_use). Changing the amount, date or type of a document in a closed month (old or new date) needs ?adjustment_reason= (else 409 FINANZAS_PERIOD_CLOSED). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500

Cuerpo

CampoTipoObligatorioRestricciones
total_clpinteger—> 0
issue_datestring—patrón ^\d{4}-\d{2}-\d{2}$
issuer_rutstring | null—máx. 20
issuer_namestring | null—máx. 200
contraparte_idstring | null—
dte_typeinteger | null—≥ 1, ≤ 999
foliostring | null—patrón ^\d{1,12}$
descriptionstring | null—máx. 300
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/documents/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "total_clp": 850000,
    "issue_date": "2026-10-02"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-ae6f-7071-c283-9e0f1a2b3c4d",
    "kind": "manual_expense",
    "source": "manual",
    "dte_type": null,
    "dte_type_label": null,
    "is_credit_note": false,
    "folio": "1042",
    "issue_date": "2026-10-01",
    "period": "2026-10",
    "received_on": null,
    "issuer_rut_formatted": "76.086.428-5",
    "receiver_rut_formatted": null,
    "counterparty_name": "Inmobiliaria Los Andes SpA",
    "contraparte": {
      "id": "0190a3c2-6a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "name": "Inmobiliaria Los Andes SpA",
      "rut_formatted": "76.086.428-5"
    },
    "amounts": {
      "net": 0,
      "exempt": 0,
      "iva": 0,
      "other_taxes": 0,
      "total": 850000
    },
    "due_date": null,
    "payment_state": "por_pagar",
    "settlement": null,
    "sii_estado_contable": null,
    "missing_at_sii": false,
    "voided": false,
    "adjusts": null,
    "f29": null,
    "honorario": null,
    "created_at": "2026-10-30T05:31:12.000Z",
    "updated_at": "2026-10-30T05:31:12.000Z",
    "adjusted_by": [],
    "description": "Arriendo oficina octubre",
    "files": [
      {
        "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
        "status": "confirmed",
        "channel": "upload",
        "filename": "boleta-arriendo-octubre.pdf",
        "mime_type": "application/pdf",
        "byte_size": 184233,
        "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
        "url_expires_at": "2026-10-30T15:10:00.000Z",
        "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
        "case_id": null,
        "email": null,
        "extraction_status": "complete",
        "extracted": {
          "total_clp": 850000,
          "currency": "CLP",
          "amount_original": null,
          "fx_rate_clp": null,
          "issue_date": "2026-10-01",
          "issuer_rut": "76086428-5",
          "issuer_name": "Inmobiliaria Los Andes SpA",
          "dte_type": null,
          "folio": "1042",
          "description": "Arriendo oficina octubre"
        },
        "missing": [],
        "error": null,
        "document_id": "0190a3c2-ae6f-7071-c283-9e0f1a2b3c4d",
        "created_at": "2026-10-30T15:00:00.000Z",
        "confirmed_at": "2026-10-30T15:02:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/documents/{id}/reopen

Reopen a settled document («Reabrir»)

Undoes what settled a document without a bank link (settlement): «Pagado antes de Vitrina» (a document from a period closed before Vitrina, still open there) or a person's «Marcar pagada». From then on it is an open document like any other: its state follows its reconciliation links, it enters the cash projection or «Vencidas sin pago visto», and the import never settles it again. Resultado and the Contador do not move (they never read the payment state). Idempotent: an open document answers reopened: false. Reopening a document a person marked anulado in a closed month needs ?adjustment_reason= (else 409 FINANZAS_PERIOD_CLOSED). Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/documents/<id>/reopen \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "reopened": true,
    "document": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
      "kind": "purchase",
      "source": "sii",
      "dte_type": 33,
      "dte_type_label": "Factura electrónica",
      "is_credit_note": false,
      "folio": "45",
      "issue_date": "2026-10-10",
      "period": "2026-10",
      "received_on": "2026-10-11",
      "issuer_rut_formatted": "76.086.428-5",
      "receiver_rut_formatted": "76.543.219-4",
      "counterparty_name": "Laboratorio Andes SpA",
      "contraparte": {
        "id": "0190a3c2-6a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "name": "Laboratorio Andes SpA",
        "rut_formatted": "76.086.428-5"
      },
      "amounts": {
        "net": 10000,
        "exempt": 0,
        "iva": 1900,
        "other_taxes": 0,
        "total": 11900
      },
      "due_date": null,
      "payment_state": "por_pagar",
      "settlement": null,
      "sii_estado_contable": "REGISTRO",
      "missing_at_sii": false,
      "voided": false,
      "adjusts": null,
      "f29": null,
      "honorario": null,
      "created_at": "2026-10-30T05:31:12.000Z",
      "updated_at": "2026-10-30T05:31:12.000Z",
      "adjusted_by": []
    }
  }
}

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

GET /finanzas/documents/f29

F29 by period, with the next due date

The monthly IVA declaration (F29) per tax period, read from the SII through the connected sociedad: amount to pay (amounts.total), f29.state (proposal / in progress / filed / rejected), filing folio and date, rectifications, the IVA / PPM breakdown, the due_date and payment_state (paid once the tax payment movement reconciles against it). next_due is the earliest unpaid F29 still due, or the next period's due date with amount: null while it is unknown. Due dates are the 20th of the next month, moved to the next working day. Read-only: Vitrina does not file the F29. from/to are months (YYYY-MM; default the last 12), at most 24. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/documents/f29 \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "from": "2025-11",
    "to": "2026-10",
    "periods": [
      {
        "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
        "kind": "tax",
        "source": "sii",
        "dte_type": null,
        "dte_type_label": null,
        "is_credit_note": false,
        "folio": null,
        "issue_date": "2026-09-01",
        "period": "2026-09",
        "received_on": null,
        "issuer_rut_formatted": "76.543.219-4",
        "receiver_rut_formatted": null,
        "counterparty_name": "Servicio de Impuestos Internos",
        "contraparte": null,
        "amounts": {
          "net": 0,
          "exempt": 0,
          "iva": 0,
          "other_taxes": 0,
          "total": 1373000
        },
        "due_date": "2026-10-20",
        "payment_state": "por_pagar",
        "settlement": null,
        "sii_estado_contable": null,
        "missing_at_sii": false,
        "voided": false,
        "adjusts": null,
        "f29": {
          "period": "2026-09",
          "state": "filed",
          "state_label": "Declarado",
          "filed": true,
          "amount_is_final": true,
          "filed_at": "2026-10-14T15:02:11.000Z",
          "filing_folio": "8123456789",
          "rectifications": 0,
          "summary": {
            "iva_debito": 1900000,
            "iva_credito": 650000,
            "iva_retenido": 0,
            "remanente_anterior": 0,
            "remanente_siguiente": 0,
            "ppm": 123000
          }
        },
        "honorario": null,
        "created_at": "2026-10-30T05:31:12.000Z",
        "updated_at": "2026-10-30T05:31:12.000Z"
      }
    ],
    "next_due": {
      "period": "2026-10",
      "due_date": "2026-11-20",
      "amount": null,
      "document_id": null,
      "state": null
    }
  }
}

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

GET /finanzas/documents/honorarios/summary

Monthly honorarios and retención total

The boletas de honorarios of one month (period, AAAA-MM; default the current month), received and issued: count, gross, retención and líquido, annulled boletas excluded. f29_codigo_151_clp is the retención on the received ones (F29 código 151). clave_status other than activa means zeros are «unknown», not «none»: boletas sync only with the SII clave connected. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
periodquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/documents/honorarios/summary \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "period": "2026-10",
    "currency": "CLP",
    "received": {
      "count": 12,
      "gross_clp": 18400000,
      "retention_clp": 2806000,
      "net_clp": 15594000
    },
    "issued": {
      "count": 0,
      "gross_clp": 0,
      "retention_clp": 0,
      "net_clp": 0
    },
    "f29_codigo_151_clp": 2806000,
    "clave_status": "activa"
  }
}

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

GET /finanzas/documents/libro

Monthly libro de compras or libro de ventas

Monthly totals of one register: kind=purchase is the libro de compras, kind=sale the libro de ventas. Per month: document count, net, exempt, IVA, other taxes, total and iva_fiscal (IVA crédito fiscal on purchases — REGISTRO entries only — or IVA débito fiscal on sales), with a line per DTE type. Notas de crédito subtract; documents the SII no longer lists and voided ones are left out. from/to are months (YYYY-MM; default January → this month), at most 24. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
kindquerypurchase \salesí
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/documents/libro \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "kind": "purchase",
    "from": "2026-10",
    "to": "2026-10",
    "months": [
      {
        "period": "2026-10",
        "count": 2,
        "net": 90000,
        "exempt": 0,
        "iva": 17100,
        "other_taxes": 0,
        "total": 107100,
        "iva_fiscal": 17100,
        "lines": [
          {
            "dte_type": 33,
            "dte_type_label": "Factura electrónica",
            "count": 1,
            "net": 100000,
            "exempt": 0,
            "iva": 19000,
            "other_taxes": 0,
            "total": 119000
          },
          {
            "dte_type": 61,
            "dte_type_label": "Nota de crédito electrónica",
            "count": 1,
            "net": -10000,
            "exempt": 0,
            "iva": -1900,
            "other_taxes": 0,
            "total": -11900
          }
        ]
      }
    ],
    "totals": {
      "count": 2,
      "net": 90000,
      "exempt": 0,
      "iva": 17100,
      "other_taxes": 0,
      "total": 107100,
      "iva_fiscal": 17100
    }
  }
}

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

GET /finanzas/documents/libro/export

Export the libro de compras or ventas (xlsx / csv)

The libro as a spreadsheet: sheet «Resumen» (one row per month and a total) and sheet «Detalle» (one row per document). format is xlsx (default) or csv (the summary only). Same query as the libro. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:export.

ParámetroEnTipoObligatorioRestricciones
kindquerypurchase \salesí
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
formatqueryxlsx \csvno
curl https://api.vitrinadev.com/api/v1/finanzas/documents/libro/export \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/documents/uploads

List uploaded and forwarded files

Every file received for Finanzas, newest first: uploads and emails forwarded to the workspace address. status=pending_review is «Por revisar» (read, waiting for a person: an upload not yet confirmed, or an email whose reading was incomplete). Each carries a short-lived signed url. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
statusquerypending_review \confirmed \discarded \
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/documents/uploads \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
      "status": "pending_review",
      "channel": "upload",
      "filename": "boleta-arriendo-octubre.pdf",
      "mime_type": "application/pdf",
      "byte_size": 184233,
      "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
      "url_expires_at": "2026-10-30T15:10:00.000Z",
      "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
      "case_id": null,
      "email": null,
      "extraction_status": "complete",
      "extracted": {
        "total_clp": 850000,
        "currency": "CLP",
        "amount_original": null,
        "fx_rate_clp": null,
        "issue_date": "2026-10-01",
        "issuer_rut": "76086428-5",
        "issuer_name": "Inmobiliaria Los Andes SpA",
        "dte_type": null,
        "folio": "1042",
        "description": "Arriendo oficina octubre"
      },
      "missing": [],
      "error": null,
      "document_id": null,
      "created_at": "2026-10-30T15:00:00.000Z",
      "confirmed_at": null
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    }
  }
}

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

POST /finanzas/documents/uploads

Upload a document that is not in the SII

A receipt, a foreign invoice (Meta Ads, software), a boleta from someone without e-boleta, a contract: a PDF or a photo (JPG, PNG, WEBP, HEIC; 15 MB at most), optionally onto a movement (movement_id) or a «Por resolver» case (case_id). The file is stored privately and READ: total, date, issuer RUT and name, type, folio, currency (USD converts at the dólar observado of its day). The answer is the file in pending_review with extracted and what is missing: nothing is in the ledger until it is confirmed (POST …/uploads/\{id\}/confirm). 422 FINANZAS_DOCUMENT_INVALID (unsupported_file, file_mismatch, file_too_large).

Or a JSON body (application/json) — what an MCP connector sends through call_operation, which cannot build multipart: \{ filename, content, content_encoding?, content_type? \} plus the operation's own form fields. content is UTF-8 text by default, or base64 when content_encoding is "base64" (images, PDF, audio, video). The same byte cap applies to the decoded bytes (413 above it); invalid base64 is a 400; the content type defaults from the filename extension. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

Cuerpo

CampoTipoObligatorioRestricciones
filenamestringsímín. 1, máx. 255
contentstringsímín. 1, máx. 35000000
content_encodingutf8 \base64—
content_typestring—máx. 200
movement_iduuid—
case_iduuid—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/documents/uploads \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "(binary)",
    "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
    "status": "pending_review",
    "channel": "upload",
    "filename": "boleta-arriendo-octubre.pdf",
    "mime_type": "application/pdf",
    "byte_size": 184233,
    "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
    "url_expires_at": "2026-10-30T15:10:00.000Z",
    "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
    "case_id": null,
    "email": null,
    "extraction_status": "complete",
    "extracted": {
      "total_clp": 850000,
      "currency": "CLP",
      "amount_original": null,
      "fx_rate_clp": null,
      "issue_date": "2026-10-01",
      "issuer_rut": "76086428-5",
      "issuer_name": "Inmobiliaria Los Andes SpA",
      "dte_type": null,
      "folio": "1042",
      "description": "Arriendo oficina octubre"
    },
    "missing": [],
    "error": null,
    "document_id": null,
    "created_at": "2026-10-30T15:00:00.000Z",
    "confirmed_at": null
  }
}

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

DELETE /finanzas/documents/uploads/{id}

Discard a file

Throws away a pending_review file: the stored file is deleted, the row stays for the log. 409 FINANZAS_DOCUMENT_CONFLICT when it is already a document. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/documents/uploads/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/documents/uploads/{id}

Get an uploaded or forwarded file

One file with what was read from it and a fresh signed url. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/documents/uploads/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
    "status": "pending_review",
    "channel": "upload",
    "filename": "boleta-arriendo-octubre.pdf",
    "mime_type": "application/pdf",
    "byte_size": 184233,
    "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
    "url_expires_at": "2026-10-30T15:10:00.000Z",
    "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
    "case_id": null,
    "email": null,
    "extraction_status": "complete",
    "extracted": {
      "total_clp": 850000,
      "currency": "CLP",
      "amount_original": null,
      "fx_rate_clp": null,
      "issue_date": "2026-10-01",
      "issuer_rut": "76086428-5",
      "issuer_name": "Inmobiliaria Los Andes SpA",
      "dte_type": null,
      "folio": "1042",
      "description": "Arriendo oficina octubre"
    },
    "missing": [],
    "error": null,
    "document_id": null,
    "created_at": "2026-10-30T15:00:00.000Z",
    "confirmed_at": null
  }
}

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

POST /finanzas/documents/uploads/{id}/confirm

Confirm a file as a document

Turns a pending_review file into a document (source manual) with its Contraparte, and returns the «Por resolver» case it now sits in (case_id) so the match can be confirmed. Every body field overrides what was read (edited reports the changes; the audit log keeps both). Contraparte: contraparte_id > the issuer RUT > the movement's or case's Contraparte > the issuer name. A factura (types 33/34/46/56/61…) with an issuer RUT is a purchase (kind: purchase) and dedupes with the SII register on (RUT, type, folio); anything else is a manual_expense. A file whose (RUT, type, folio) is already in the ledger, or with document_id, is ATTACHED to that document (outcome: attached). 422 FINANZAS_DOCUMENT_INVALID (incomplete with details.missing, amount_invalid, rut_invalid); 409 FINANZAS_DOCUMENT_CONFLICT (already_confirmed, already_discarded, duplicate). A new document dated in a closed month needs ?adjustment_reason= (else 409 FINANZAS_PERIOD_CLOSED, nothing written) and is logged as an «ajuste posterior al cierre». Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
adjustment_reasonquerystringnomín. 3, máx. 500

Cuerpo

CampoTipoObligatorioRestricciones
total_clpinteger—> 0
issue_datestring—patrón ^\d{4}-\d{2}-\d{2}$
issuer_rutstring | null—máx. 20
issuer_namestring | null—máx. 200
contraparte_idstring | null—
dte_typeinteger | null—≥ 1, ≤ 999
foliostring | null—patrón ^\d{1,12}$
descriptionstring | null—máx. 300
net_clpinteger | null—≥ 0
iva_clpinteger | null—≥ 0
document_idstring | null—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/documents/uploads/<id>/confirm \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "total_clp": 850000,
    "issue_date": "2026-10-01",
    "description": "Arriendo oficina octubre"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "outcome": "created",
    "document": {
      "id": "0190a3c2-ae6f-7071-c283-9e0f1a2b3c4d",
      "kind": "manual_expense",
      "source": "manual",
      "dte_type": null,
      "dte_type_label": null,
      "is_credit_note": false,
      "folio": "1042",
      "issue_date": "2026-10-01",
      "period": "2026-10",
      "received_on": null,
      "issuer_rut_formatted": "76.086.428-5",
      "receiver_rut_formatted": null,
      "counterparty_name": "Inmobiliaria Los Andes SpA",
      "contraparte": {
        "id": "0190a3c2-6a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "name": "Inmobiliaria Los Andes SpA",
        "rut_formatted": "76.086.428-5"
      },
      "amounts": {
        "net": 0,
        "exempt": 0,
        "iva": 0,
        "other_taxes": 0,
        "total": 850000
      },
      "due_date": null,
      "payment_state": "por_pagar",
      "settlement": null,
      "sii_estado_contable": null,
      "missing_at_sii": false,
      "voided": false,
      "adjusts": null,
      "f29": null,
      "honorario": null,
      "created_at": "2026-10-30T05:31:12.000Z",
      "updated_at": "2026-10-30T05:31:12.000Z",
      "adjusted_by": [],
      "description": "Arriendo oficina octubre",
      "files": [
        {
          "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
          "status": "confirmed",
          "channel": "upload",
          "filename": "boleta-arriendo-octubre.pdf",
          "mime_type": "application/pdf",
          "byte_size": 184233,
          "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
          "url_expires_at": "2026-10-30T15:10:00.000Z",
          "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
          "case_id": null,
          "email": null,
          "extraction_status": "complete",
          "extracted": {
            "total_clp": 850000,
            "currency": "CLP",
            "amount_original": null,
            "fx_rate_clp": null,
            "issue_date": "2026-10-01",
            "issuer_rut": "76086428-5",
            "issuer_name": "Inmobiliaria Los Andes SpA",
            "dte_type": null,
            "folio": "1042",
            "description": "Arriendo oficina octubre"
          },
          "missing": [],
          "error": null,
          "document_id": "0190a3c2-ae6f-7071-c283-9e0f1a2b3c4d",
          "created_at": "2026-10-30T15:00:00.000Z",
          "confirmed_at": "2026-10-30T15:02:00.000Z"
        }
      ]
    },
    "file": {
      "id": "0190a3c2-8c4d-7e5f-a061-7c8d9e0f1a2b",
      "status": "confirmed",
      "channel": "upload",
      "filename": "boleta-arriendo-octubre.pdf",
      "mime_type": "application/pdf",
      "byte_size": 184233,
      "url": "https://files.example.test/tenants/…/finanzas/files/0190a3c2.pdf?X-Amz-Signature=…",
      "url_expires_at": "2026-10-30T15:10:00.000Z",
      "movement_id": "0190a3c2-9d5e-7f60-b172-8d9e0f1a2b3c",
      "case_id": null,
      "email": null,
      "extraction_status": "complete",
      "extracted": {
        "total_clp": 850000,
        "currency": "CLP",
        "amount_original": null,
        "fx_rate_clp": null,
        "issue_date": "2026-10-01",
        "issuer_rut": "76086428-5",
        "issuer_name": "Inmobiliaria Los Andes SpA",
        "dte_type": null,
        "folio": "1042",
        "description": "Arriendo oficina octubre"
      },
      "missing": [],
      "error": null,
      "document_id": "0190a3c2-ae6f-7071-c283-9e0f1a2b3c4d",
      "created_at": "2026-10-30T15:00:00.000Z",
      "confirmed_at": "2026-10-30T15:02:00.000Z"
    },
    "case_id": "0190a3c2-bf70-7182-d394-0f1a2b3c4d5e",
    "edited": {}
  }
}

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

POST /finanzas/expenses/from-movements

Create gastos from many bank movements («Crear gasto»)

For each cargo (money out), creates one manual gasto (a document of kind manual_expense, paid, dated on the movement’s day, with its Contraparte) classified to the account. Up to 500 movements in ONE transaction: an abono (movement_not_outbound), a movement that already has a gasto (409 expense_exists), is reconciled (409 movement_linked) or is classified (409 movement_classified) refuses the whole batch, with details.movement_ids. Name the account by account_id or in plain language with account_name (name or path, no case/accents); an ambiguous name answers 422 account_ambiguous with details.candidates to choose from. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/expenses/from-movements \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "movement_ids": [
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
      "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02"
    ],
    "account_name": "Mantenimiento",
    "description": "Jardinero, sin boleta"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "count": 2,
    "expenses": [
      {
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a10",
        "amount_clp": 60000,
        "day": "2026-10-03"
      },
      {
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
        "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5a11",
        "amount_clp": 60000,
        "day": "2026-10-17"
      }
    ]
  }
}

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

GET /finanzas/howto-videos

«Cómo se usa» videos for the Finanzas screens

The Finanzas screens that have a how-to video, in nav order, each with the link and the embed_url to play in a dialog. A screen with no video is absent (show no «Cómo se usa» button there). The list is managed by Vitrina and is the same for every workspace. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/howto-videos \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "videos": [
      {
        "screen": "resumen",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "embed_url": "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
      }
    ]
  }
}

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

GET /finanzas/inbox

The workspace's forwarding address

The address to forward receipts and invoices to (finanzas+&lt;token&gt;@vitrinadev.com). Mail is accepted only from an active member of the workspace (their login email) who holds finanzas:write (a read-only seat such as the Contador is dropped) and whose provider signed it (DKIM / SPF / DMARC); anything else is dropped and logged. Each PDF or photo attached (or the email body, when there is none) is read; a complete reading becomes a document at once, an incomplete one waits in GET /finanzas/documents/uploads?status=pending_review. Created on first read. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/inbox \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "address": "[email protected]",
    "accepts_from": "workspace_members",
    "created_at": "2026-10-30T15:00:00.000Z",
    "rotated_at": null
  }
}

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

POST /finanzas/inbox/rotate

Replace the forwarding address

A new address; the old one stops working at once (mail to it is dropped). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

Cuerpo

CampoTipoObligatorioRestricciones
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/inbox/rotate \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ejemplo de respuesta (200)

{
  "data": {
    "address": "[email protected]",
    "accepts_from": "workspace_members",
    "created_at": "2026-10-30T15:00:00.000Z",
    "rotated_at": "2026-10-30T16:00:00.000Z"
  }
}

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

GET /finanzas/inherited-overdue

Vencido heredado (retired): the payables to review

Retired (Finanzas 50): nothing writes «vencido heredado» any more; the payables it held are «Pagado antes de Vitrina» (GET /finanzas/documents?paid_before_vitrina=true), and only rows written before it in a workspace outside that backfill still show here. It listed the payables that arrived already more than 90 days overdue (payment_state = vencido_heredado): they are out of the cash-flow projection and the balances until someone marks them pagado or anulado. Each carries the open bank payment that most likely settled it (suggested_match: exact amount, a payment is suggested to one document only; signal says why). summary counts the whole filter, items is one page. Filter by Contraparte with contraparte. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» and no glosa on money in. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
contrapartequeryuuidno
sortquerydue_date \amountno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "summary": {
      "count": 1135,
      "amount_clp": 437600000
    },
    "filter": {
      "contraparte_id": null
    },
    "items": [
      {
        "id": "01930000-0000-7000-8000-0000000000d1",
        "kind": "purchase",
        "dte_type": 33,
        "label": "Factura electrónica N° 451",
        "folio": "451",
        "contraparte": {
          "id": "01930000-0000-7000-8000-0000000000c1",
          "name": "Laboratorio Dental Andes Ltda"
        },
        "counterparty_name": "Laboratorio Dental Andes Ltda",
        "issue_date": "2025-06-10",
        "due_date": "2025-07-10",
        "arrived_on": "2025-11-02",
        "days_overdue": 120,
        "total_clp": 302852,
        "outstanding_clp": 302852,
        "suggested_match": {
          "movement_id": "01930000-0000-7000-8000-0000000000a1",
          "day": "2025-09-12",
          "amount_clp": 302852,
          "description": "Transferencia a Laboratorio Dental Andes",
          "counterparty_name": "Laboratorio Dental Andes Ltda",
          "bank_account_id": "01930000-0000-7000-8000-0000000000b1",
          "bank_account_name": "Cuenta corriente",
          "institution_name": "BCI",
          "number_masked": "•••• 9819",
          "signal": "contraparte"
        }
      }
    ],
    "total": 1135,
    "limit": 50,
    "offset": 0
  }
}

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

GET /finanzas/inherited-overdue/batches

Settle batches a person made

Each bulk action a person made («Marcar pagada», Mes en paralelo), newest first; «Pagado antes de Vitrina» is the import's, never listed here, with how many of its documents are still resolved (live_count). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
limitqueryintegerno≥ 1, ≤ 100
curl https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue/batches \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "batch_id": "01930000-0000-7000-8000-0000000000b9",
      "resolution": "pagado",
      "resolved_at": "2026-11-08T14:00:00.000Z",
      "resolved_by": "00000000-0000-4000-8000-000000000001",
      "document_count": 3,
      "live_count": 3,
      "amount_clp": 908556
    }
  ]
}

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

POST /finanzas/inherited-overdue/batches/{batchId}/undo

Undo a settle batch («Deshacer»)

Puts the documents of a settle batch (a person's «Marcar pagada», or a Mes en paralelo review) back as they were (a payment linked in the meantime wins and the state follows it). To reopen one document, POST /finanzas/documents/{id}/reopen. Pass document_ids to undo only some. Idempotent: what was already undone comes back in skipped. Audited as finanzas.inherited_overdue.undo. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
batchIdpathuuidsí

Cuerpo

CampoTipoObligatorioRestricciones
document_idsuuid[]—
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue/batches/<id>/undo \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ejemplo de respuesta (200)

{
  "data": {
    "batch_id": "01930000-0000-7000-8000-0000000000b9",
    "resolution": "pagado",
    "undone": [
      "01930000-0000-7000-8000-0000000000d1"
    ],
    "skipped": [],
    "remaining": {
      "count": 1135,
      "amount_clp": 437600000
    }
  }
}

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

POST /finanzas/inherited-overdue/resolve

Vencido heredado (retired): mark pagado or anulado in bulk

Retired (Finanzas 50): only rows still «vencido heredado» (written before it, outside the Finanzas 50 backfill) can be resolved; anything else comes back skipped (not_inherited). It resolved up to 500 inherited payables at once. pagado: it was settled; the document leaves the review as paid (no bank link is invented). anulado: it never has to be paid; the document is voided, so no report counts it. A document that is no longer inherited is skipped (not_inherited), never overwritten. One batch, one batch_id: it lands in the audit log (finanzas.inherited_overdue.pagado / .anulado) and POST /finanzas/inherited-overdue/batches/\{batchId\}/undo puts it back. «anulado» in a closed month needs ?adjustment_reason= (409 FINANZAS_PERIOD_CLOSED otherwise). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

Cuerpo

CampoTipoObligatorioRestricciones
document_idsuuid[]sí
resolutionpagado \anuladosí
notestring—máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue/resolve \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document_ids": [
      "01930000-0000-7000-8000-0000000000d1"
    ],
    "resolution": "pagado"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "batch_id": "01930000-0000-7000-8000-0000000000b9",
    "resolution": "pagado",
    "resolved": [
      "01930000-0000-7000-8000-0000000000d1"
    ],
    "skipped": [],
    "amount_clp": 302852,
    "remaining": {
      "count": 1134,
      "amount_clp": 437297148
    }
  }
}

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

GET /finanzas/months

Cierre de mes: each month and its state

The month selector, newest first: open (a «Por resolver» case belongs to the month, or it has not ended), ready_to_close (ended, nothing open), closed (a person closed it) or closed_imported («Cerrado antes de Vitrina»: the import closed it; it raises no cases and a change asks for a reason; closed_source chipax, imported_at). A case belongs to ONE month: the one of its oldest item (a movement, or a document to classify). Default: the current month and the 11 before; at most 24. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/months \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "current_month": "2026-10",
    "months": [
      {
        "month": "2026-10",
        "label": "octubre 2026",
        "state": "open",
        "ended": false,
        "open_cases": 7,
        "open_cases_amount_clp": 2418300,
        "closed_at": null,
        "closed_by": null,
        "closed_source": null,
        "imported_at": null,
        "adjustment_count": 0
      },
      {
        "month": "2026-09",
        "label": "septiembre 2026",
        "state": "closed",
        "ended": true,
        "open_cases": 0,
        "open_cases_amount_clp": 0,
        "closed_at": "2026-10-03T14:20:00.000Z",
        "closed_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
        "closed_source": "person",
        "imported_at": null,
        "adjustment_count": 1
      },
      {
        "month": "2026-08",
        "label": "agosto 2026",
        "state": "ready_to_close",
        "ended": true,
        "open_cases": 0,
        "open_cases_amount_clp": 0,
        "closed_at": null,
        "closed_by": null,
        "closed_source": null,
        "imported_at": null,
        "adjustment_count": 0
      }
    ]
  }
}

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

GET /finanzas/months/{month}

Cierre de mes: one month, its close checklist and its close

The state, the checklist the close shows (open cases, automatic actions nobody reviewed in «Hecho por Vitrina», unclassified cost documents, movements nothing explains), can_close / blocking_reason, and once closed: who and when, the snapshot of the month's figures at close and what changed since (changes_since_close: logged adjustments, documents and movements that arrived after the close, documents voided after it). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/months/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-09",
    "label": "septiembre 2026",
    "from": "2026-09-01",
    "to": "2026-09-30",
    "state": "closed",
    "ended": true,
    "open_cases": 0,
    "open_cases_amount_clp": 0,
    "closed_at": "2026-10-03T14:20:00.000Z",
    "closed_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
    "closed_source": "person",
    "imported_at": null,
    "closed_by_name": "Ana Pérez",
    "adjustment_count": 1,
    "can_close": false,
    "blocking_reason": "already_closed",
    "checklist": {
      "open_cases": 0,
      "open_cases_amount_clp": 0,
      "open_case_movements_count": 0,
      "open_case_documents_count": 0,
      "unreviewed_count": 4,
      "unclassified_count": 0,
      "unmatched_count": 0,
      "unmatched_movements_count": 0,
      "unmatched_documents_count": 0,
      "unmatched_pending_clp": 0,
      "unmatched_movements_pending_clp": 0,
      "unmatched_documents_pending_clp": 0
    },
    "close": {
      "unreviewed_count": 4,
      "unclassified_count": 0,
      "unmatched_count": 0,
      "snapshot_version": 1,
      "snapshot": {
        "documents": {
          "purchase": {
            "count": 38,
            "total_clp": 9840000
          },
          "sale": {
            "count": 412,
            "total_clp": 31250000
          }
        },
        "movements": {
          "inbound": {
            "count": 220,
            "total_clp": 30100000
          },
          "outbound": {
            "count": 95,
            "total_clp": 21400000
          }
        },
        "accounts": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
            "count": 12,
            "total_clp": 2380000
          },
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
            "count": 5,
            "total_clp": 640000
          }
        ],
        "unclassified_documents": {
          "count": 0
        },
        "unmatched_movements": {
          "count": 0,
          "pending_clp": 0
        },
        "unmatched_documents": {
          "count": 0,
          "pending_clp": 0
        }
      }
    },
    "changes_since_close": {
      "adjustments": 1,
      "documents_added": 1,
      "documents_voided": 0,
      "movements_added": 0
    }
  }
}

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

GET /finanzas/months/{month}/adjustments

Ajustes posteriores al cierre of a month

Every change to the month after it was closed, newest first: a person's (with the reason they gave) or an automatic one (rule, match, ai, import; no reason). before / after carry the classification lines; amount_clp the amount of a link. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/months/<id>/adjustments \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-09",
    "closed": true,
    "closed_at": "2026-10-03T14:20:00.000Z",
    "total": 1,
    "items": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a90",
        "month": "2026-09",
        "change": "reclassify",
        "label": "Reclasificado de Laboratorio a Insumos clínicos",
        "actor": "person",
        "actor_user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
        "actor_name": "Ana Pérez",
        "reason": "La factura era de insumos, no de laboratorio",
        "item": {
          "type": "document",
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01"
        },
        "document": {
          "kind": "purchase",
          "dte_type": 33,
          "folio": "4410",
          "issue_date": "2026-09-05",
          "total_clp": 450000,
          "counterparty_name": "Proveedor Ejemplo SpA"
        },
        "movement": null,
        "before": [
          {
            "account": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
              "name": "Laboratorio",
              "path": "Costos directos › Laboratorio"
            },
            "amount_clp": 450000,
            "period": "2026-09"
          }
        ],
        "after": [
          {
            "account": {
              "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
              "name": "Insumos clínicos",
              "path": "Costos directos › Insumos clínicos"
            },
            "amount_clp": 450000,
            "period": "2026-09"
          }
        ],
        "amount_clp": null,
        "created_at": "2026-10-12T16:05:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/months/{month}/close

Close a month (Cerrar mes)

Closes an ended month with nothing open in «Por resolver». Send the checklist.unreviewed_count the person confirmed. Records who, when, the checklist and a snapshot of the figures. Soft: nothing is locked; from now on a change that touches the month needs ?adjustment_reason= and is listed as an «ajuste posterior al cierre». 409 FINANZAS_MONTH_NOT_CLOSABLE with details.reason open_cases (+ open_cases, the count), not_ended, already_closed or unreviewed_changed (+ unreviewed_count, the count now). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

Connected apps: refused with 403 CONNECTED_APP_EXCLUDED_OPERATION and absent from the MCP catalogue, whatever write pack was granted — the «Agentes de IA» pack promises not to include this act. An API key or a personal token of the workspace holding the scope reaches it as usual.

ParámetroEnTipoObligatorioRestricciones
monthpathstringsípatrón ^\d{4}-(0[1-9]|1[0-2])$

Cuerpo

CampoTipoObligatorioRestricciones
unreviewed_countintegersí≥ 0
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/months/<id>/close \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "unreviewed_count": 4
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "month": "2026-09",
    "label": "septiembre 2026",
    "from": "2026-09-01",
    "to": "2026-09-30",
    "state": "closed",
    "ended": true,
    "open_cases": 0,
    "open_cases_amount_clp": 0,
    "closed_at": "2026-10-03T14:20:00.000Z",
    "closed_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01",
    "closed_source": "person",
    "imported_at": null,
    "closed_by_name": "Ana Pérez",
    "adjustment_count": 0,
    "can_close": false,
    "blocking_reason": "already_closed",
    "checklist": {
      "open_cases": 0,
      "open_cases_amount_clp": 0,
      "open_case_movements_count": 0,
      "open_case_documents_count": 0,
      "unreviewed_count": 4,
      "unclassified_count": 0,
      "unmatched_count": 0,
      "unmatched_movements_count": 0,
      "unmatched_documents_count": 0,
      "unmatched_pending_clp": 0,
      "unmatched_movements_pending_clp": 0,
      "unmatched_documents_pending_clp": 0
    },
    "close": {
      "unreviewed_count": 4,
      "unclassified_count": 0,
      "unmatched_count": 0,
      "snapshot_version": 1,
      "snapshot": {
        "documents": {
          "purchase": {
            "count": 38,
            "total_clp": 9840000
          },
          "sale": {
            "count": 412,
            "total_clp": 31250000
          }
        },
        "movements": {
          "inbound": {
            "count": 220,
            "total_clp": 30100000
          },
          "outbound": {
            "count": 95,
            "total_clp": 21400000
          }
        },
        "accounts": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
            "count": 12,
            "total_clp": 2380000
          },
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
            "count": 5,
            "total_clp": 640000
          }
        ],
        "unclassified_documents": {
          "count": 0
        },
        "unmatched_movements": {
          "count": 0,
          "pending_clp": 0
        },
        "unmatched_documents": {
          "count": 0,
          "pending_clp": 0
        }
      }
    },
    "changes_since_close": {
      "adjustments": 0,
      "documents_added": 0,
      "documents_voided": 0,
      "movements_added": 0
    }
  }
}

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

GET /finanzas/movements

List bank and card movements (Movimientos)

One register of every bank and card movement of the workspace across all its accounts: incoming (inbound, abono) and outgoing (outbound, cargo), CLP and USD (a USD movement carries its original amount in cents and the CLP equivalent at that day's dólar observado). Each movement has its account, Contraparte, reconciliation state (open | partial | reconciled), the amount still to assign (pending_clp) and who resolved it. Filters: account, day range, direction, state, amount range (whole CLP), free text, Contraparte. Newest first; offset-paginated; meta.totals sums the in and out amounts of every match; meta.cash_pending is «efectivo por depositar», the same object the Flujo de caja returns. A movement is cash, never income. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
bank_account_idqueryuuidno
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
directionqueryinbound \outboundno
statequeryopen \partial \reconciled
amount_minqueryinteger | nullno≥ 0, ≤ 9007199254740991
amount_maxqueryinteger | nullno≥ 0, ≤ 9007199254740991
qquerystringnomáx. 200
contraparte_idqueryuuidno
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/movements \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
      "date": "2026-10-29",
      "direction": "outbound",
      "amount_clp": 1190000,
      "currency": "CLP",
      "original": null,
      "description": "TRANSF A PROVEEDOR EJEMPLO SPA",
      "counterparty_name": "Proveedor Ejemplo SpA",
      "counterparty_rut_formatted": "76.123.456-7",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
        "name": "Proveedor Ejemplo SpA",
        "rut_formatted": "76.123.456-7"
      },
      "bank_account": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "name": "Cuenta corriente",
        "institution_name": "Banco de Chile",
        "number_masked": "****4821",
        "currency": "CLP"
      },
      "state": "partial",
      "assigned_clp": 700000,
      "pending_clp": 490000,
      "resolved_by": "match",
      "source": "bank_feed",
      "operation_id": null,
      "payer": null,
      "comment_count": 1,
      "ads_document_id": null,
      "card_processor": null,
      "generic_label": null,
      "created_at": "2026-10-30T08:00:00.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0
    },
    "totals": {
      "inbound_clp": 0,
      "outbound_clp": 1190000
    },
    "cash_pending": {
      "amount_clp": 11648107,
      "window_days": 60,
      "expected_until": "2026-11-06"
    }
  }
}

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

GET /finanzas/movements/{id}

Get a movement with its comments

One movement of the register, with its staff comments (oldest first). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/movements/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
    "date": "2026-10-29",
    "direction": "outbound",
    "amount_clp": 1190000,
    "currency": "CLP",
    "original": null,
    "description": "TRANSF A PROVEEDOR EJEMPLO SPA",
    "counterparty_name": "Proveedor Ejemplo SpA",
    "counterparty_rut_formatted": "76.123.456-7",
    "contraparte": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
      "name": "Proveedor Ejemplo SpA",
      "rut_formatted": "76.123.456-7"
    },
    "bank_account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
      "name": "Cuenta corriente",
      "institution_name": "Banco de Chile",
      "number_masked": "****4821",
      "currency": "CLP"
    },
    "state": "partial",
    "assigned_clp": 700000,
    "pending_clp": 490000,
    "resolved_by": "match",
    "source": "bank_feed",
    "operation_id": null,
    "payer": null,
    "comment_count": 1,
    "ads_document_id": null,
    "card_processor": null,
    "generic_label": null,
    "created_at": "2026-10-30T08:00:00.000Z",
    "comments": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
        "body": "Pago de la factura 4521 de Proveedor Ejemplo SpA.",
        "author": {
          "user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
          "name": "Camila Rojas"
        },
        "created_at": "2026-10-30T15:20:00.000Z"
      }
    ]
  }
}

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

GET /finanzas/movements/{id}/comments

List a movement's comments

Staff comments on a movement, with author and time, oldest first. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/movements/<id>/comments \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
      "body": "Pago de la factura 4521 de Proveedor Ejemplo SpA.",
      "author": {
        "user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
        "name": "Camila Rojas"
      },
      "created_at": "2026-10-30T15:20:00.000Z"
    }
  ]
}

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

POST /finanzas/movements/{id}/comments

Comment on a movement

Adds a staff comment (1–2000 characters) to a movement; the author is the calling member. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/movements/<id>/comments \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Pago de la factura 4521 de Proveedor Ejemplo SpA."
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
    "body": "Pago de la factura 4521 de Proveedor Ejemplo SpA.",
    "author": {
      "user_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02",
      "name": "Camila Rojas"
    },
    "created_at": "2026-10-30T15:20:00.000Z"
  }
}

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

GET /finanzas/own-account-suggestions

Own-account cards: «Agregar ··XXXX como cuenta propia»

D5: money that goes to the workspace's own RUT or between two of its accounts is a «Traspaso entre cuentas propias» that Vitrina applies by itself (in Hecho por Vitrina, undoable), never income, never a cost and never a case. When the destination account is not registered yet, one card offers to add it. Lists the destination accounts that live traspasos went to and that are neither registered nor dismissed, biggest first, with the masked number, the total and the count. An account disappears once registered (accept), dismissed («Ahora no») or when all its traspasos were undone. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/own-account-suggestions \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "suggestions": [
      {
        "key": "4521",
        "last4": "4521",
        "number_masked": "•••• 4521",
        "bank_name": null,
        "total_clp": 9950000,
        "count": 3,
        "first_day": "2026-10-13",
        "last_day": "2026-11-05",
        "title": "Agregar ··4521 como cuenta propia",
        "body": "Le transferiste $9.950.000 a tu propio RUT. Si la cuenta es tuya, esos traspasos no cuentan como gasto."
      }
    ],
    "count": 1
  }
}

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

POST /finanzas/own-account-suggestions/{key}/accept

Add the suggested account as an own account

D5: money that goes to the workspace's own RUT or between two of its accounts is a «Traspaso entre cuentas propias» that Vitrina applies by itself (in Hecho por Vitrina, undoable), never income, never a cost and never a case. When the destination account is not registered yet, one card offers to add it. Adds the account to the registry as a Finanzas bank account (Cuentas; the same registry the agent shares from); its traspasos stay traspasos. Idempotent: an account already registered under that number is returned as it is. 404 NOT_FOUND when no traspaso went to that number. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
keypathstringsípatrón ^[1-9][0-9]{3,39}$
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/own-account-suggestions/<id>/accept \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
    "name": "Cuenta ··4521"
  }
}

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

POST /finanzas/own-account-suggestions/{key}/dismiss

Hide the own-account card for that account («Ahora no»)

D5: money that goes to the workspace's own RUT or between two of its accounts is a «Traspaso entre cuentas propias» that Vitrina applies by itself (in Hecho por Vitrina, undoable), never income, never a cost and never a case. When the destination account is not registered yet, one card offers to add it. The card never comes back for that account; its traspasos stay traspasos. Idempotent. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
keypathstringsípatrón ^[1-9][0-9]{3,39}$
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/own-account-suggestions/<id>/dismiss \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "key": "4521",
    "dismissed": true
  }
}

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

GET /finanzas/payment-terms

Default payment terms per document class

Ajustes › Plazos de pago: the workspace default terms in days for suppliers (compras), customers (ventas), honorarios and expenses (gastos). customized: false = the built-in default. A document is due its issue date plus the payment terms in days: the Contraparte's own terms, else the workspace default of its class (suppliers = compras, customers = ventas, honorarios, expenses = gastos; built-in 30 / 30 / 0 / 0). Changing terms recomputes the due date of every open document it covers; a paid one keeps its due date, and so does one whose source printed a due date. Taxes and notas de crédito have none. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/payment-terms \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "class": "suppliers",
      "label": "Proveedores",
      "days": 30,
      "customized": false,
      "source": "default",
      "updated_at": null
    },
    {
      "class": "customers",
      "label": "Clientes",
      "days": 30,
      "customized": false,
      "source": "default",
      "updated_at": null
    },
    {
      "class": "honorarios",
      "label": "Honorarios",
      "days": 15,
      "customized": true,
      "source": "chipax",
      "updated_at": "2026-10-30T12:00:00.000Z"
    },
    {
      "class": "expenses",
      "label": "Gastos",
      "days": 0,
      "customized": false,
      "source": "default",
      "updated_at": null
    }
  ]
}

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

PATCH /finanzas/payment-terms

Change default payment terms

Send only the classes that change (0–365 days each). Every open document of a changed class whose Contraparte has no terms of its own gets a new due date. A document is due its issue date plus the payment terms in days: the Contraparte's own terms, else the workspace default of its class (suppliers = compras, customers = ventas, honorarios, expenses = gastos; built-in 30 / 30 / 0 / 0). Changing terms recomputes the due date of every open document it covers; a paid one keeps its due date, and so does one whose source printed a due date. Taxes and notas de crédito have none. Audited. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write (Ajustes).

curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/payment-terms \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "honorarios": 15
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "defaults": [
      {
        "class": "suppliers",
        "label": "Proveedores",
        "days": 30,
        "customized": false,
        "source": "default",
        "updated_at": null
      },
      {
        "class": "customers",
        "label": "Clientes",
        "days": 30,
        "customized": false,
        "source": "default",
        "updated_at": null
      },
      {
        "class": "honorarios",
        "label": "Honorarios",
        "days": 15,
        "customized": true,
        "source": "chipax",
        "updated_at": "2026-10-30T12:00:00.000Z"
      },
      {
        "class": "expenses",
        "label": "Gastos",
        "days": 0,
        "customized": false,
        "source": "default",
        "updated_at": null
      }
    ],
    "recomputed_documents": 4
  }
}

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

GET /finanzas/possible-duplicates

Posibles duplicados: bank lines that repeat

Bank lines that repeat under one bank-issued operation id on the same account, day, direction and amount, at least min_repeats times (default 3). An id derived from the date (ending in six zeros) is not a bank reference and never counts. READ-ONLY: Vitrina deletes nothing here; someone reviews the list before any change. The Chipax import skips a repeated line of this kind on its next run. A caller without the patient register (the Contador) sees a patient as «Paciente A. P.» and no glosa on money in. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
min_repeatsqueryintegerno≥ 2, ≤ 10
curl https://api.vitrinadev.com/api/v1/finanzas/possible-duplicates \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "min_repeats": 3,
    "group_count": 1,
    "line_count": 3,
    "groups": [
      {
        "key": "acct|2025121010599320|2527239|outbound|2025-12-10",
        "day": "2025-12-10",
        "amount_clp": 2527239,
        "direction": "outbound",
        "bank_operation": "2025121010599320",
        "description": "Pk smart4d tasa int. 2,09%",
        "repeats": 3,
        "total_clp": 7581717,
        "bank_account": {
          "id": "01930000-0000-7000-8000-0000000000b2",
          "name": "Itaú tarjeta de crédito",
          "institution_name": "Itaú",
          "number_masked": "•••• 5402"
        },
        "lines": [
          {
            "id": "01930000-0000-7000-8000-0000000000a1",
            "source": "chipax",
            "description": "Pk smart4d tasa int. 2,09%"
          }
        ]
      }
    ]
  }
}

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

GET /finanzas/produccion

Producción, cobrado y boleteado (clinics)

The clinic’s bridge per month and for the range: producción (treatments performed), cobrado (patient payments received) and boleteado (boletas DTE 39/41 issued, at totals, minus notas de crédito on boletas), with por cobrar (produced, not paid) and anticipos (paid, not produced) per patient, so that producción − por cobrar + anticipos = cobrado; the range column nets per patient. Clinic money authority: a clinic mirrored from Dentalink/Medilink reads producción and cobrado only from the mirror (source: clinic_mirror), otherwise from Vitrina’s registers; bank movements are never income. Boletas are checked against payments both ways (payments_without_boleta, boletas_without_payment): first the link the clinic system holds, then a boleta to the payment’s own patient, then a monthly summary boleta (to the generic receiver 66.666.666-6 or to nobody, named by no payment) covers its month’s remaining payments (monthly_boletas); a month’s leftover is then covered by what the NEXT month’s summary has left (month-end cash is billed in the next summary), shown as the signed boleteado_otro_mes_clp, and a leftover whose next summary is not issued yet is resumen_pendiente, not a gap. payments_without_boleta is also grouped by_month (per_month items each); sin_boleta_month lists one month completely. ingresos_resultado_clp is the Resultado’s income for the same months, to read next to it. A caller without the patient roster sees «Paciente A. P.» and no RUT. Other verticals answer is_clinic: false with empty figures. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
limitqueryintegerno≥ 1, ≤ 200
per_monthqueryintegerno≥ 1, ≤ 50
sin_boleta_monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/produccion \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "from": "2026-10",
    "to": "2026-10",
    "current_month": "2026-11",
    "currency": "CLP",
    "is_clinic": true,
    "source": "clinic_mirror",
    "boletas_source": "finanzas",
    "months": [
      {
        "month": "2026-10",
        "label": "octubre 2026",
        "proforma": false,
        "produccion_clp": 61340000,
        "cobrado_clp": 58204222,
        "boleteado_clp": 57726146,
        "por_cobrar_clp": 4955778,
        "anticipos_clp": 1820000,
        "cobrado_sin_boleta_clp": 478076,
        "boletas_sin_pago_clp": 0,
        "ingresos_resultado_clp": 57726146,
        "pagos_sin_boleta": 3,
        "pendiente_resumen_clp": 0,
        "pagos_pendiente_resumen": 0,
        "boleteado_otro_mes_clp": 0
      }
    ],
    "total": {
      "produccion_clp": 61340000,
      "cobrado_clp": 58204222,
      "boleteado_clp": 57726146,
      "por_cobrar_clp": 4955778,
      "anticipos_clp": 1820000,
      "cobrado_sin_boleta_clp": 478076,
      "boletas_sin_pago_clp": 0,
      "ingresos_resultado_clp": 57726146,
      "pagos_sin_boleta": 3,
      "pendiente_resumen_clp": 0,
      "pagos_pendiente_resumen": 0,
      "boleteado_otro_mes_clp": 0
    },
    "payments_without_boleta": {
      "count": 3,
      "total_clp": 478076,
      "items": [
        {
          "payment_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7101",
          "source": "clinic_mirror",
          "day": "2026-10-31",
          "amount_clp": 250000,
          "without_boleta_clp": 250000,
          "reason": "sin_boleta",
          "folios": [],
          "patient": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7301",
            "name": "Paciente A. P.",
            "rut": null
          }
        }
      ],
      "by_month": [
        {
          "month": "2026-10",
          "count": 3,
          "total_clp": 478076,
          "items": [
            {
              "payment_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7101",
              "source": "clinic_mirror",
              "day": "2026-10-31",
              "amount_clp": 250000,
              "without_boleta_clp": 250000,
              "reason": "sin_boleta",
              "folios": [],
              "patient": {
                "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7301",
                "name": "Paciente A. P.",
                "rut": null
              }
            }
          ]
        }
      ]
    },
    "boletas_without_payment": {
      "count": 0,
      "total_clp": 0,
      "items": []
    },
    "monthly_boletas": [
      {
        "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f7201",
        "dte_type": 41,
        "folio": "1204",
        "day": "2026-10-31",
        "month": "2026-10",
        "total_clp": 57726146,
        "covered_clp": 57726146,
        "payments": 412
      }
    ]
  }
}

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

GET /finanzas/reconciliation/cash-pending

Efectivo por depositar

Per business day, the cash not yet in a bank: from Caja del día (the day's cash payments) when the workspace records payments in Vitrina, else the day's boletas minus what payment processors settled for that day. remaining_clp drops as deposits link to it. only_open (default true) hides deposited days. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
toquerystringnopatrón ^\d{4}-\d{2}-\d{2}$
only_openquerytrue \falseno
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/cash-pending \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
        "day": "2026-10-28",
        "source": "boletas",
        "amount_clp": 400000,
        "deposited_clp": 0,
        "remaining_clp": 400000,
        "detail": {
          "boletas_clp": 2406000,
          "processors_clp": 2006000,
          "transfers_clp": 0
        }
      }
    ],
    "total_remaining_clp": 400000,
    "pending": {
      "amount_clp": 400000,
      "window_days": 60,
      "expected_until": "2026-11-06"
    }
  }
}

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

GET /finanzas/reconciliation/commissions

Payout commission lines

What Mercado Pago or a card acquirer (Transbank) kept on each payout of the period: Mercado Pago's own fee when it reports one, else a card batch's gross minus its deposit. Each line is classified to the plan de cuentas' payment-processor commission account (account, null when the plan has none). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringsípatrón ^\d{4}-\d{2}-\d{2}$
toquerystringsípatrón ^\d{4}-\d{2}-\d{2}$
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/commissions \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "items": [
      {
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a12",
        "day": "2026-10-28",
        "amount_clp": 71400,
        "source": "mercado_pago",
        "bank_account_name": "Cuenta Mercado Pago",
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b12",
          "name": "Comisiones de pasarelas de pago"
        }
      }
    ],
    "total_clp": 71400
  }
}

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

GET /finanzas/reconciliation/documents/{id}

A document's payments and candidates («Asignar pago»)

A document with what is paid (paid_clp), what remains (outstanding_clp) and its derived payment_state (por_pagar, parcial, pagado; for a sale read «por cobrar»), its links, and the open movements that could pay it (prefiltered by folio, Contraparte and amount, or by q). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
qquerystringnomáx. 200
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/documents/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "document": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
      "kind": "purchase",
      "dte_type": 33,
      "folio": "4521",
      "issue_date": "2026-10-01",
      "due_date": "2026-10-31",
      "counterparty_name": "Proveedor Ejemplo SpA",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
        "name": "Proveedor Ejemplo SpA"
      },
      "total_clp": 1190000,
      "paid_clp": 700000,
      "outstanding_clp": 490000,
      "payment_state": "parcial"
    },
    "links": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e01",
        "target": "document",
        "amount_clp": 700000,
        "actor": "match",
        "actor_user_id": null,
        "reasons": [
          "folio_and_amount"
        ],
        "note": null,
        "created_at": "2026-10-30T08:05:00.000Z",
        "undone_at": null,
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
        "document": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
          "kind": "purchase",
          "dte_type": 33,
          "folio": "4521",
          "issue_date": "2026-10-01",
          "total_clp": 1190000,
          "counterparty_name": "Proveedor Ejemplo SpA"
        },
        "payment": null,
        "paired_movement": null,
        "cash_day": null,
        "contraparte": null,
        "payout": null,
        "undoable": true
      }
    ],
    "candidates": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a11",
        "day": "2026-10-30",
        "direction": "outbound",
        "amount_clp": 490000,
        "pending_clp": 490000,
        "description": "TRANSF A PROVEEDOR EJEMPLO SPA",
        "counterparty_name": "Proveedor Ejemplo SpA",
        "bank_account_name": "Cuenta corriente",
        "signal": "contraparte",
        "suggested_amount_clp": 490000
      }
    ]
  }
}

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

POST /finanzas/reconciliation/links

Link a movement (Conciliar)

Links one movement to one or more targets, atomically: documents (partial amounts allowed), a payment, the other half of an own-account transfer («Traspaso entre cuentas propias»: both close, neither counts in the result), a third party («Cuenta de terceros», excluded from the result) or days of «efectivo por depositar». amount_clp omitted = what remains on both sides; the amounts may not exceed what remains of the movement nor of each document (400). A Caja payment is a clinic settlement and goes alone. The document payment state is re-derived. «Clasificar sin documento» is not a link: it is the movement's classification (PUT /finanzas/classifications/\{type\}/\{id\}, type movements). Returns the movement's reconciliation. Audited. Clinic money authority: while a clinic is mirrored from its clinic system, an incoming movement only links to mirrored payments, as evidence; it never creates a payment or income (a sale-document link answers 400). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
adjustment_reasonquerystringnomín. 3, máx. 500

Cuerpo

CampoTipoObligatorioRestricciones
movement_iduuidsí
linksany[]sí
notestring | null—máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/reconciliation/links \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
    "links": [
      {
        "target": "document",
        "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
        "amount_clp": 700000
      },
      {
        "target": "third_party",
        "amount_clp": 490000
      }
    ],
    "note": "Abono parcial a la factura 4521; el resto es de un socio."
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "movement": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
      "date": "2026-10-29",
      "direction": "outbound",
      "amount_clp": 1190000,
      "currency": "CLP",
      "original": null,
      "description": "TRANSF A PROVEEDOR EJEMPLO SPA FACT 4521",
      "counterparty_name": "Proveedor Ejemplo SpA",
      "counterparty_rut_formatted": "76.123.456-7",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
        "name": "Proveedor Ejemplo SpA",
        "rut_formatted": "76.123.456-7"
      },
      "bank_account": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "name": "Cuenta corriente",
        "institution_name": "Banco de Chile",
        "number_masked": "****4821",
        "currency": "CLP"
      },
      "state": "reconciled",
      "assigned_clp": 1190000,
      "pending_clp": 0,
      "resolved_by": "match",
      "source": "bank_feed",
      "operation_id": null,
      "payer": null,
      "comment_count": 0,
      "ads_document_id": null,
      "card_processor": null,
      "generic_label": null,
      "created_at": "2026-10-30T08:00:00.000Z"
    },
    "money_mirrored": false,
    "payer": null,
    "links": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e01",
        "target": "document",
        "amount_clp": 700000,
        "actor": "person",
        "actor_user_id": null,
        "reasons": [
          "linked_by_staff"
        ],
        "note": null,
        "created_at": "2026-10-30T08:05:00.000Z",
        "undone_at": null,
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
        "document": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
          "kind": "purchase",
          "dte_type": 33,
          "folio": "4521",
          "issue_date": "2026-10-01",
          "total_clp": 1190000,
          "counterparty_name": "Proveedor Ejemplo SpA"
        },
        "payment": null,
        "paired_movement": null,
        "cash_day": null,
        "contraparte": null,
        "payout": null,
        "undoable": true
      }
    ],
    "legacy_links": [],
    "commission": null
  }
}

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

POST /finanzas/reconciliation/links/{id}/undo

Undo a link (Deshacer)

Undoes a link: both sides get the amount back and the document state is re-derived. The link stays with undone_at (audited), and the engine never re-applies that pair. A clinic settlement (settlement:&lt;id&gt; from legacy_links) runs the clinic «Deshacer»; a payment credited from the bank's comprobante answers 409. 409 when already undone. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

ParámetroEnTipoObligatorioRestricciones
idpathstringsípatrón ^(?:(?:settlement|payment):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
adjustment_reasonquerystringnomín. 3, máx. 500
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/reconciliation/links/<id>/undo \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
    "document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01"
  }
}

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

GET /finanzas/reconciliation/movements/{id}

A movement's reconciliation: links, payer, commission

What explains a bank movement: its Finanzas links (document, mirrored payment, own-account transfer, third party, cash deposit), each with its amount and actor (person Manual, rule Regla, match Coincidencia, ai IA), undone ones included (undone_at); the clinic links that predate Finanzas (legacy_links: settlements and payments credited from a comprobante); a Mercado Pago movement's payer (name, e-mail, and the patient when one matches); and the commission a payment processor kept on a payout, with its plan-de-cuentas account. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/movements/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "movement": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
      "date": "2026-10-29",
      "direction": "outbound",
      "amount_clp": 1190000,
      "currency": "CLP",
      "original": null,
      "description": "TRANSF A PROVEEDOR EJEMPLO SPA FACT 4521",
      "counterparty_name": "Proveedor Ejemplo SpA",
      "counterparty_rut_formatted": "76.123.456-7",
      "contraparte": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
        "name": "Proveedor Ejemplo SpA",
        "rut_formatted": "76.123.456-7"
      },
      "bank_account": {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a6c",
        "name": "Cuenta corriente",
        "institution_name": "Banco de Chile",
        "number_masked": "****4821",
        "currency": "CLP"
      },
      "state": "reconciled",
      "assigned_clp": 1190000,
      "pending_clp": 0,
      "resolved_by": "match",
      "source": "bank_feed",
      "operation_id": null,
      "payer": null,
      "comment_count": 0,
      "ads_document_id": null,
      "card_processor": null,
      "generic_label": null,
      "created_at": "2026-10-30T08:00:00.000Z"
    },
    "money_mirrored": false,
    "payer": null,
    "links": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4e01",
        "target": "document",
        "amount_clp": 1190000,
        "actor": "match",
        "actor_user_id": null,
        "reasons": [
          "folio_and_amount"
        ],
        "note": null,
        "created_at": "2026-10-30T08:05:00.000Z",
        "undone_at": null,
        "movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
        "document": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
          "kind": "purchase",
          "dte_type": 33,
          "folio": "4521",
          "issue_date": "2026-10-01",
          "total_clp": 1190000,
          "counterparty_name": "Proveedor Ejemplo SpA"
        },
        "payment": null,
        "paired_movement": null,
        "cash_day": null,
        "contraparte": null,
        "payout": null,
        "undoable": true
      }
    ],
    "legacy_links": [],
    "commission": null
  }
}

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

GET /finanzas/reconciliation/movements/{id}/candidates

Candidates to reconcile a movement («Conciliar»)

Open documents (right direction, with what each still owes), payments (a mirrored clinic payment, or a Caja payment for a native clinic), the other half of an own-account transfer (same amount, another own account, ±5 days) and days of «efectivo por depositar» (incoming movements). Without q, documents are prefiltered by folio in the description, Contraparte (including its linked RUTs and accounts) and amount; with q, a free search. pending_clp is what remains to assign. Clinic money authority: while a clinic is mirrored from its clinic system, an incoming movement only links to mirrored payments, as evidence; it never creates a payment or income (a sale-document link answers 400). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
qquerystringnomáx. 200
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/movements/<id>/candidates \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "pending_clp": 1190000,
    "money_mirrored": false,
    "documents": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01",
        "kind": "purchase",
        "dte_type": 33,
        "folio": "4521",
        "issue_date": "2026-10-01",
        "due_date": "2026-10-31",
        "total_clp": 1190000,
        "outstanding_clp": 1190000,
        "counterparty_name": "Proveedor Ejemplo SpA",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a20",
          "name": "Proveedor Ejemplo SpA"
        },
        "signal": "folio",
        "suggested_amount_clp": 1190000
      }
    ],
    "payments": [],
    "transfers": [],
    "cash_days": []
  }
}

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

POST /finanzas/reconciliation/run

Run the reconciliation engine for a period

Runs the deterministic engine over the open movements of a period (at most one year): gastos created from a movement («Crear gasto»), own-account transfers, cash deposits against «efectivo por depositar», mirrored clinic payments by the payer the bank or Mercado Pago names, and documents by folio, RUT (with the Contraparte's linked RUTs and accounts), amount and date window. It applies only a unique match (actor match, «Coincidencia») and never re-applies a pair a person undid; the rest stays for «Por resolver». It also runs on its own after every bank and SII sync. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:write.

Cuerpo

CampoTipoObligatorioRestricciones
fromstringsípatrón ^\d{4}-\d{2}-\d{2}$
tostringsípatrón ^\d{4}-\d{2}-\d{2}$
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/reconciliation/run \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-10-01",
    "to": "2026-10-31"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "skipped": false,
    "from": "2026-10-01",
    "to": "2026-10-31",
    "movements_seen": 214,
    "applied": {
      "documents": 37,
      "transfers": 4,
      "cash_deposits": 6,
      "payments": 52,
      "expenses": 3
    },
    "ambiguous": 9
  }
}

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

GET /finanzas/resultado

Resultado: the operating result by month

Reportes › Resultado on the plan de cuentas, one column per month (months) plus a total. Income = the boletas and facturas de venta ISSUED in the month, by issue date, paid or not. Every amount is NET: a sale at neto + exento (IVA débito is not income), a purchase without its recoverable IVA crédito fiscal and specific taxes (IVA no recuperable is a cost; a boleta or a document with no IVA breakdown counts in full); classification lines are prorated to net; movements count in full. «Por conciliar» on the ingresos row is the part not yet matched to money received, at document totals. A classified item counts in the accrual month of each classification line; an unclassified document in its issue month («Ingresos / Egresos sin clasificar»). Notas de crédito subtract; boletas de honorarios count gross (the retención is a tax owed, not a smaller cost). «Egresos sin documento» (classified money out with no legal document) subtracts in the resultado operacional; «Retiros» sit below it, outside the result. A closed month carries post_close («ajuste posterior al cierre»). The current month is proforma. Default: January of the current year to the current month; at most 24 months. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
curl https://api.vitrinadev.com/api/v1/finanzas/resultado \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "from": "2026-09",
    "to": "2026-10",
    "current_month": "2026-10",
    "currency": "CLP",
    "months": [
      {
        "month": "2026-09",
        "label": "septiembre 2026",
        "proforma": false,
        "closed": true,
        "post_close": {
          "closed_at": "2026-10-03T14:20:00.000Z",
          "adjustment_count": 1,
          "changed": true,
          "result_delta_clp": -180000,
          "accounts": [
            {
              "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5104",
              "name": "Laboratorios",
              "path": "Costos de explotación › Laboratorios",
              "at_close_clp": 2380000,
              "now_clp": 2560000,
              "delta_clp": 180000
            }
          ]
        }
      },
      {
        "month": "2026-10",
        "label": "octubre 2026",
        "proforma": true,
        "closed": false,
        "post_close": null
      }
    ],
    "rows": [
      {
        "key": "ingresos",
        "type": "section",
        "label": "Ingresos",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": 1,
        "values": [
          31250000,
          18400000
        ],
        "total": 49650000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": {
          "values": [
            1200000,
            6400000
          ],
          "total": 7600000,
          "counts": [
            3,
            41
          ],
          "count_total": 44
        },
        "undocumented": null,
        "by_account": null,
        "drillable": false
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5101",
        "type": "account",
        "label": "Ingresos de explotación",
        "parent_key": "ingresos",
        "depth": 2,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5101",
          "kind": "ingreso",
          "path": "Ingresos › Ingresos de explotación",
          "archived": false,
          "has_children": true
        },
        "sign": 1,
        "values": [
          30800000,
          17900000
        ],
        "total": 48700000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5102",
        "type": "account",
        "label": "Atención odontológica",
        "parent_key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5101",
        "depth": 3,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5102",
          "kind": "ingreso",
          "path": "Ingresos › Ingresos de explotación › Atención odontológica",
          "archived": false,
          "has_children": false
        },
        "sign": 1,
        "values": [
          30800000,
          17900000
        ],
        "total": 48700000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "ingresos_sin_clasificar",
        "type": "line",
        "label": "Ingresos sin clasificar",
        "parent_key": "ingresos",
        "depth": 1,
        "account": null,
        "sign": 1,
        "values": [
          450000,
          500000
        ],
        "total": 950000,
        "counts": [
          2,
          3
        ],
        "count_total": 5,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "costos",
        "type": "section",
        "label": "Costos",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": -1,
        "values": [
          9840000,
          5100000
        ],
        "total": 14940000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": false
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5103",
        "type": "account",
        "label": "Costos de explotación",
        "parent_key": "costos",
        "depth": 1,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5103",
          "kind": "costo",
          "path": "Costos de explotación",
          "archived": false,
          "has_children": true
        },
        "sign": -1,
        "values": [
          9840000,
          5100000
        ],
        "total": 14940000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5104",
        "type": "account",
        "label": "Laboratorios",
        "parent_key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5103",
        "depth": 2,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5104",
          "kind": "costo",
          "path": "Costos de explotación › Laboratorios",
          "archived": false,
          "has_children": false
        },
        "sign": -1,
        "values": [
          2560000,
          1300000
        ],
        "total": 3860000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "margen_bruto",
        "type": "subtotal",
        "label": "Margen bruto",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": 0,
        "values": [
          21410000,
          13300000
        ],
        "total": 34710000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": [
          68.5,
          72.3
        ],
        "margin_pct_total": 69.8,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": false
      },
      {
        "key": "gastos",
        "type": "section",
        "label": "Gastos",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": -1,
        "values": [
          6200000,
          4100000
        ],
        "total": 10300000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": false
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5105",
        "type": "account",
        "label": "Gastos de administración y venta",
        "parent_key": "gastos",
        "depth": 1,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5105",
          "kind": "gasto",
          "path": "Gastos de administración y venta",
          "archived": false,
          "has_children": true
        },
        "sign": -1,
        "values": [
          6200000,
          4100000
        ],
        "total": 10300000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5106",
        "type": "account",
        "label": "Arriendo",
        "parent_key": "account:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5105",
        "depth": 3,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5106",
          "kind": "gasto",
          "path": "Gastos de administración y venta › Gastos generales › Arriendo",
          "archived": false,
          "has_children": false
        },
        "sign": -1,
        "values": [
          2400000,
          2400000
        ],
        "total": 4800000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "egresos_sin_documento",
        "type": "line",
        "label": "Egresos sin documento",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": -1,
        "values": [
          620000,
          380000
        ],
        "total": 1000000,
        "counts": [
          7,
          4
        ],
        "count_total": 11,
        "alert": true,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": [
          {
            "account_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5107",
            "name": "Mantenimientos (equipos y computación)",
            "path": "Gastos de administración y venta › Gastos generales › Mantenimientos (equipos y computación)",
            "kind": "gasto",
            "total": 1000000
          }
        ],
        "drillable": true
      },
      {
        "key": "egresos_sin_clasificar",
        "type": "line",
        "label": "Egresos sin clasificar",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": -1,
        "values": [
          0,
          310000
        ],
        "total": 310000,
        "counts": [
          0,
          2
        ],
        "count_total": 2,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": true
      },
      {
        "key": "resultado_operacional",
        "type": "subtotal",
        "label": "Resultado operacional",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": 0,
        "values": [
          14590000,
          8510000
        ],
        "total": 23100000,
        "counts": null,
        "count_total": null,
        "alert": false,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": null,
        "by_account": null,
        "drillable": false
      },
      {
        "key": "retiros",
        "type": "line",
        "label": "Retiros",
        "parent_key": null,
        "depth": 0,
        "account": null,
        "sign": 0,
        "values": [
          3000000,
          3000000
        ],
        "total": 6000000,
        "counts": [
          1,
          1
        ],
        "count_total": 2,
        "alert": true,
        "margin_pct": null,
        "margin_pct_total": null,
        "por_conciliar": null,
        "undocumented": {
          "values": [
            0,
            3000000
          ],
          "total": 3000000,
          "counts": [
            0,
            1
          ],
          "count_total": 1
        },
        "by_account": null,
        "drillable": true
      }
    ],
    "summary": {
      "resultado_operacional_clp": 23100000,
      "ingresos_clp": 49650000,
      "por_conciliar_clp": 7600000,
      "egresos_sin_documento": {
        "count": 11,
        "total_clp": 1000000
      },
      "retiros_clp": 6000000,
      "retiros_sin_documento": {
        "count": 1,
        "total_clp": 3000000
      }
    },
    "waterfall": [
      {
        "key": "ingresos",
        "label": "Ingresos",
        "amount_clp": 49650000,
        "running_clp": 49650000
      },
      {
        "key": "costos",
        "label": "Costos",
        "amount_clp": -14940000,
        "running_clp": 34710000
      },
      {
        "key": "gastos",
        "label": "Gastos",
        "amount_clp": -10300000,
        "running_clp": 24410000
      },
      {
        "key": "egresos_sin_documento",
        "label": "Egresos sin documento",
        "amount_clp": -1000000,
        "running_clp": 23410000
      },
      {
        "key": "egresos_sin_clasificar",
        "label": "Egresos sin clasificar",
        "amount_clp": -310000,
        "running_clp": 23100000
      },
      {
        "key": "resultado_operacional",
        "label": "Resultado operacional",
        "amount_clp": 23100000,
        "running_clp": 23100000
      }
    ]
  }
}

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

GET /finanzas/resultado/export

Export the resultado (xlsx / csv)

The resultado as a spreadsheet: sheet «Resultado», exactly the rows and months the screen shows (plus «Por conciliar», «Margen bruto %», «Retiros sin documento» and one line per month with an «ajuste posterior al cierre»), and sheet «Detalle», every document and movement behind each line with its net («En el resultado») and its total (xlsx only; csv is the first sheet). Same query as the resultado; format xlsx (default) or csv. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read and finanzas:export.

ParámetroEnTipoObligatorioRestricciones
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
formatqueryxlsx \csvno
curl https://api.vitrinadev.com/api/v1/finanzas/resultado/export \
  -H "Authorization: Bearer $VITRINA_KEY"

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

GET /finanzas/resultado/items

Resultado: the documents and movements behind a line

The drill-down of one row (row; account needs account_id, and a group account includes every account under it) for one month (month, a cell) or a range (from/to, the total column). One item per document or movement, with its signed contribution to the line (amount_clp, net) and at totals (gross_amount_clp), newest first; sum_clp equals the cell. A document carries result_clp, what it counts for in the resultado, next to its total_clp. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_reports:read.

ParámetroEnTipoObligatorioRestricciones
rowqueryaccount \ingresos_sin_clasificar \egresos_sin_documento \
account_idqueryuuidno
monthquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
fromquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
toquerystringnopatrón ^\d{4}-(0[1-9]|1[0-2])$
limitqueryintegerno≥ 1, ≤ 200
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/finanzas/resultado/items \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "row": "egresos_sin_documento",
    "account": null,
    "from": "2026-10",
    "to": "2026-10",
    "total": 1,
    "sum_clp": 380000,
    "gross_sum_clp": 380000,
    "items": [
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5201",
        "day": "2026-10-14",
        "periods": [
          "2026-10"
        ],
        "amount_clp": 380000,
        "gross_amount_clp": 380000,
        "counterparty_name": "Juan Soto",
        "description": "Jardín octubre",
        "account_ids": [
          "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5107"
        ],
        "accounts": [
          {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5107",
            "name": "Mantenimientos (equipos y computación)",
            "path": "Gastos de administración y venta › Gastos generales › Mantenimientos (equipos y computación)"
          }
        ],
        "documented": false,
        "accepted_without_document": true,
        "document": null,
        "movement": {
          "direction": "outbound",
          "amount_clp": 380000
        }
      }
    ]
  }
}

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

GET /finanzas/resumen

Resumen: the Finanzas home, three answers and the context

The Finanzas home in one read, every figure from its detail screen: cash («¿cuánto me queda a fin de mes?») = the Flujo de caja projection — today’s balance of the operating accounts, the month-end and 30-day balance, the lowest point and runs_out (the first day under zero within 30 days, with its biggest payment); result («¿cuánto gasto? / ¿este mes gana plata?») = Costos y equilibrio for the current month at net amounts (ingresos, gastos = variables + fijos, «sin clasificar» apart and counted as variable, resultado operacional) against the punto de equilibrio of the last closed month; inbox («Te necesita») = the open «Por resolver» cases and the previous month’s close state; automatic = this month’s «Hecho por Vitrina» totals and the newest 3; accounts with their balances. problem is the one thing shown in red (cash running out, else a bank connection that needs a person). A caller without finanzas_reports:read gets cash, result and every account balance (balance_clp, balance_as_of) null; account names stay (reports_visible: false). A Contador sees patients as initials. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

curl https://api.vitrinadev.com/api/v1/finanzas/resumen \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "as_of": "2026-10-30",
    "month": "2026-10",
    "month_label": "octubre 2026",
    "currency": "CLP",
    "reports_visible": true,
    "synced_at": "2026-10-30T11:02:00.000Z",
    "problem": {
      "kind": "cash_runs_out",
      "date": "2026-11-06",
      "balance_clp": -412000
    },
    "cash": {
      "today_clp": 4917109,
      "balance_as_of": "2026-10-30T11:02:00.000Z",
      "accounts_count": 3,
      "unknown_count": 0,
      "savings_clp": 12000000,
      "month_end": {
        "date": "2026-10-31",
        "balance_clp": 5102000,
        "band": {
          "low_clp": 4980000,
          "high_clp": 5260000
        }
      },
      "in_30_days": {
        "date": "2026-11-29",
        "balance_clp": 3880000,
        "band": {
          "low_clp": 2100000,
          "high_clp": 5400000
        }
      },
      "lowest": {
        "date": "2026-11-06",
        "balance_clp": -412000,
        "negative": true,
        "is_today": false,
        "cause": {
          "key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6501",
          "source": "payable",
          "direction": "outflow",
          "label": "Factura 4410 · Laboratorio Andes SpA",
          "amount_clp": 5800000,
          "date": "2026-11-06",
          "due_date": "2026-11-06",
          "overdue": false,
          "movable": true,
          "move": null,
          "document": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6501",
            "kind": "purchase",
            "dte_type": 33,
            "folio": "4410"
          },
          "contraparte": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6601",
            "name": "Laboratorio Andes SpA"
          },
          "recurring_item_id": null,
          "rescue_id": null,
          "count": null
        }
      },
      "runs_out": {
        "date": "2026-11-06",
        "covered_until": "2026-11-05",
        "balance_clp": -412000,
        "cause": {
          "key": "document:0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6501",
          "source": "payable",
          "direction": "outflow",
          "label": "Factura 4410 · Laboratorio Andes SpA",
          "amount_clp": 5800000,
          "date": "2026-11-06",
          "due_date": "2026-11-06",
          "overdue": false,
          "movable": true,
          "move": null,
          "document": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6501",
            "kind": "purchase",
            "dte_type": 33,
            "folio": "4410"
          },
          "contraparte": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6601",
            "name": "Laboratorio Andes SpA"
          },
          "recurring_item_id": null,
          "rescue_id": null,
          "count": null
        }
      },
      "overdue": {
        "payables_clp": 0,
        "payables_count": 0,
        "stale_payables_clp": 0,
        "stale_payables_count": 0,
        "receivables_clp": 380000,
        "receivables_count": 2
      },
      "inherited_overdue": {
        "count": 0,
        "amount_clp": 0
      },
      "points": [
        {
          "date": "2026-10-30",
          "balance_clp": 4917109,
          "band": {
            "low_clp": 4917109,
            "high_clp": 4917109
          }
        },
        {
          "date": "2026-10-31",
          "balance_clp": 5102000,
          "band": {
            "low_clp": 4980000,
            "high_clp": 5260000
          }
        }
      ]
    },
    "result": {
      "month": "2026-10",
      "label": "octubre 2026",
      "proforma": true,
      "ingresos_clp": 41200000,
      "gastos_clp": 38900000,
      "variables_clp": 17400000,
      "fijos_clp": 21500000,
      "sin_clasificar_clp": 900000,
      "resultado_clp": 2300000,
      "equilibrio": {
        "month": "2026-09",
        "label": "septiembre 2026",
        "equilibrio_clp": 44600000,
        "sin_calculo": null,
        "produccion": null,
        "skipped_incompleto": null
      },
      "target_clp": 44600000,
      "cobertura_pct": 92.4,
      "brecha_clp": -3400000
    },
    "inbox": {
      "open": 4,
      "amount_at_stake_clp": 2860000,
      "by_kind": {
        "reconcile": 2,
        "transfer": 0,
        "classify": 1,
        "missing_document": 1,
        "unknown": 0
      },
      "close": {
        "month": "2026-09",
        "label": "septiembre 2026",
        "state": "closed",
        "open_cases": 0
      }
    },
    "automatic": {
      "month": "2026-10",
      "count": 240,
      "total_clp": 61200000,
      "unreviewed_count": 31,
      "by_actor": {
        "rule": 150,
        "match": 72,
        "ai": 18
      },
      "recent": [
        {
          "kind": "link",
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6701",
          "actor": "match",
          "label": "Pago de la factura 4388 de Laboratorio Andes SpA",
          "day": "2026-10-29",
          "amount_clp": 1240000,
          "applied_at": "2026-10-29T14:10:00.000Z",
          "movement": {
            "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6801",
            "day": "2026-10-29",
            "amount_clp": 1240000,
            "direction": "outbound",
            "description": "Transferencia a Laboratorio Andes"
          },
          "document": null,
          "target": "document",
          "reasons": [
            "rut",
            "amount"
          ],
          "account": null,
          "split": false,
          "rule": null,
          "reviewed_at": null,
          "reviewed_by": null,
          "group_name": "Laboratorio Andes SpA",
          "group_key": "contraparte:3f6c1d2e-5b7a-4c1e-9d0f-2a8b6c4e1f70",
          "group_count": 1,
          "group_total_clp": 1240000
        }
      ]
    },
    "accounts": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6401",
        "name": "Cuenta corriente",
        "institution_name": "Banco de Chile",
        "number_masked": "··4821",
        "type": "cuenta_corriente",
        "currency": "CLP",
        "balance_clp": 3719083,
        "balance_as_of": "2026-10-30T11:02:00.000Z",
        "in_cash": true
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f6402",
        "name": "Ahorro",
        "institution_name": "Banco de Chile",
        "number_masked": "··0057",
        "type": "cuenta_ahorro",
        "currency": "CLP",
        "balance_clp": 12000000,
        "balance_as_of": "2026-10-30T11:02:00.000Z",
        "in_cash": false
      }
    ]
  }
}

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

GET /finanzas/rules

Reglas: the classification rules

A rule: «for this Contraparte (and/or a text the description contains, an amount range, a movement direction or a document kind), classify to this account». Conditions are AND-combined; a rule needs a Contraparte or a text. target: movement classifies bank movements with no document. Rules run on new documents and movements; an item they classify carries source: rule (marker «Regla»). When several active rules match one item and disagree on the account, none applies. Newest first, each with its run count. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
statusqueryactive \paused \archived \
curl https://api.vitrinadev.com/api/v1/finanzas/rules \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "rules": [
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
        "name": "Jardinero",
        "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
        "target": "movement",
        "contraparte": null,
        "text_contains": "jardin",
        "amount_min_clp": null,
        "amount_max_clp": 200000,
        "direction": "outbound",
        "document_kind": null,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
          "name": "Mantenimiento",
          "path": "Gastos de administración › Mantenimiento",
          "kind": "gasto",
          "archived": false
        },
        "status": "active",
        "origin": "person",
        "run_count": 12,
        "last_applied_at": "2026-10-29T08:00:00.000Z",
        "created_at": "2026-09-01T12:00:00.000Z",
        "updated_at": "2026-10-29T08:00:00.000Z"
      },
      {
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c02",
        "name": null,
        "label": "Documentos de Laboratorio Dental Andes Ltda → Costos directos › Laboratorio",
        "target": "document",
        "contraparte": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5d01",
          "name": "Laboratorio Dental Andes Ltda",
          "rut": "76.123.456-0"
        },
        "text_contains": null,
        "amount_min_clp": null,
        "amount_max_clp": null,
        "direction": null,
        "document_kind": null,
        "account": {
          "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d10",
          "name": "Laboratorio",
          "path": "Costos directos › Laboratorio",
          "kind": "costo",
          "archived": false
        },
        "status": "active",
        "origin": "person",
        "run_count": 31,
        "last_applied_at": "2026-10-29T08:00:00.000Z",
        "created_at": "2026-09-01T12:00:00.000Z",
        "updated_at": "2026-10-29T08:00:00.000Z"
      }
    ]
  }
}

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

POST /finanzas/rules

Create a rule

A rule: «for this Contraparte (and/or a text the description contains, an amount range, a movement direction or a document kind), classify to this account». Conditions are AND-combined; a rule needs a Contraparte or a text. target: movement classifies bank movements with no document. Rules run on new documents and movements; an item they classify carries source: rule (marker «Regla»). When several active rules match one item and disagree on the account, none applies. Name the Contraparte by contraparte_id, contraparte_rut or contraparte_name, and the account by account_id or account_name, so an agent can fill it from plain language. from_item («Siempre clasificar así») prefills from a classified item. It does not touch existing items: preview with GET /finanzas/rules/\{id\}/pending, then POST .../apply. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jardinero",
    "target": "movement",
    "text_contains": "jardin",
    "direction": "outbound",
    "amount_max_clp": 200000,
    "account_name": "Mantenimiento"
  }'

Ejemplo de respuesta (201)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 200000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "active",
    "origin": "person",
    "run_count": 0,
    "last_applied_at": null,
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

GET /finanzas/rules/{id}

A rule

A rule: «for this Contraparte (and/or a text the description contains, an amount range, a movement direction or a document kind), classify to this account». Conditions are AND-combined; a rule needs a Contraparte or a text. target: movement classifies bank movements with no document. Rules run on new documents and movements; an item they classify carries source: rule (marker «Regla»). When several active rules match one item and disagree on the account, none applies. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/rules/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 200000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "active",
    "origin": "person",
    "run_count": 12,
    "last_applied_at": "2026-10-29T08:00:00.000Z",
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

PATCH /finanzas/rules/{id}

Edit a rule

Change any condition, the target or the account (null clears a condition). Items already classified keep their lines. An archived rule answers 409 rule_archived. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X PATCH https://api.vitrinadev.com/api/v1/finanzas/rules/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_max_clp": 250000
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» hasta $250.000 → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 250000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "active",
    "origin": "person",
    "run_count": 12,
    "last_applied_at": "2026-10-29T08:00:00.000Z",
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

POST /finanzas/rules/{id}/apply

«Aplicar a pendientes»: apply a rule to unclassified items

Classifies every unclassified item the rule matches (never a classified one) with source: rule, and adds them to its run count. An archived rule answers 409 rule_archived. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/apply \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "rule_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "applied": 4
  }
}

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

POST /finanzas/rules/{id}/archive

Archive a rule

Retires it for good (its past classifications stay); idempotent. An archived rule cannot be paused or resumed (409 rule_archived). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/archive \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 200000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "archived",
    "origin": "person",
    "run_count": 12,
    "last_applied_at": "2026-10-29T08:00:00.000Z",
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

POST /finanzas/rules/{id}/pause

Pause a rule

Stops it running on new items; idempotent. An archived rule cannot be paused or resumed (409 rule_archived). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/pause \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 200000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "paused",
    "origin": "person",
    "run_count": 12,
    "last_applied_at": "2026-10-29T08:00:00.000Z",
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

GET /finanzas/rules/{id}/pending

«Aplicar a pendientes»: preview

How many UNCLASSIFIED items the rule would classify now, their total and up to 10 of them. Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas:read.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/pending \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "rule_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "count": 4,
    "total_clp": 240000,
    "sample": [
      {
        "type": "movement",
        "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
        "day": "2026-10-03",
        "amount_clp": 60000,
        "counterparty_name": "Juan Pérez",
        "description": "Transferencia jardin octubre"
      }
    ]
  }
}

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

POST /finanzas/rules/{id}/resume

Resume a paused rule

Runs on new items again; idempotent. An archived rule cannot be paused or resumed (409 rule_archived). Requires the finanzas add-on — without it every Finanzas operation answers 402 ENTITLEMENT_NOT_ACTIVE (details.required = ["finanzas"]). Requires finanzas_admin:write.

ParámetroEnTipoObligatorioRestricciones
idpathuuidsí
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/resume \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
    "name": "Jardinero",
    "label": "Movimientos (cargos) que contienen «jardin» → Gastos de administración › Mantenimiento",
    "target": "movement",
    "contraparte": null,
    "text_contains": "jardin",
    "amount_min_clp": null,
    "amount_max_clp": 200000,
    "direction": "outbound",
    "document_kind": null,
    "account": {
      "id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d12",
      "name": "Mantenimiento",
      "path": "Gastos de administración › Mantenimiento",
      "kind": "gasto",
      "archived": false
    },
    "status": "active",
    "origin": "person",
    "run_count": 12,
    "last_applied_at": "2026-10-29T08:00:00.000Z",
    "created_at": "2026-09-01T12:00:00.000Z",
    "updated_at": "2026-10-29T08:00:00.000Z"
  }
}

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

GET /ops/finanzas-howto-videos

Manage «Cómo se usa» videos: list every screen

Every Finanzas screen with its label and its video link (null when none), plus the accepted video hosts. Vitrina's own management, not a workspace's: an operator key holding billing_ops:admin or a Vitrina staff session on the founders' console. Any other caller gets 403.

curl https://api.vitrinadev.com/api/v1/ops/finanzas-howto-videos \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "screens": [
      {
        "screen": "resumen",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "embed_url": "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ",
        "label": "Resumen"
      },
      {
        "screen": "por_resolver",
        "label": "Por resolver",
        "url": null,
        "embed_url": null
      }
    ],
    "allowed_hosts": [
      "youtube.com",
      "youtu.be",
      "vimeo.com",
      "loom.com",
      "drive.google.com"
    ]
  }
}

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

DELETE /ops/finanzas-howto-videos/{screen}

Clear a screen's «Cómo se usa» video

Removes the video of one Finanzas screen; workspaces stop showing its button. Idempotent. Vitrina's own management, not a workspace's: an operator key holding billing_ops:admin or a Vitrina staff session on the founders' console. Any other caller gets 403.

ParámetroEnTipoObligatorioRestricciones
screenpathFinanzasHowtoScreensí
curl -X DELETE https://api.vitrinadev.com/api/v1/ops/finanzas-howto-videos/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

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

PUT /ops/finanzas-howto-videos/{screen}

Set or change a screen's «Cómo se usa» video

Sets the video link of one Finanzas screen for every workspace, replacing any previous one. Only https video links from YouTube (watch, youtu.be, shorts, embed), Vimeo, Loom (share/embed) or Google Drive (file/d/…) are accepted; anything else answers 400 VALIDATION_ERROR. Vitrina's own management, not a workspace's: an operator key holding billing_ops:admin or a Vitrina staff session on the founders' console. Any other caller gets 403.

ParámetroEnTipoObligatorioRestricciones
screenpathFinanzasHowtoScreensí
curl -X PUT https://api.vitrinadev.com/api/v1/ops/finanzas-howto-videos/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  }'

Ejemplo de respuesta (200)

{
  "data": {
    "screen": "resumen",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "embed_url": "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ",
    "label": "Resumen"
  }
}

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

En esta página

GET /finanzas/accountsPOST /finanzas/accountsDELETE /finanzas/accounts/{id}PATCH /finanzas/accounts/{id}POST /finanzas/accounts/{id}/archivePOST /finanzas/accounts/{id}/restoreGET /finanzas/accounts/reviewPOST /finanzas/accounts/review/dismissGET /finanzas/ads-chargesPOST /finanzas/ads-charges/syncGET /finanzas/ads-connectionsGET /finanzas/ai/metricsGET /finanzas/ai/settingsPUT /finanzas/ai/settingsGET /finanzas/alertsGET /finanzas/automatic-actionsPOST /finanzas/automatic-actions/{kind}/{id}/undoPOST /finanzas/automatic-actions/reviewGET /finanzas/bank-accountsPOST /finanzas/bank-accountsPUT /finanzas/bank-accounts/{id}/agent-sharingDELETE /finanzas/bank-accounts/{id}/cartola-mappingGET /finanzas/bank-accounts/{id}/cartolasPOST /finanzas/bank-accounts/{id}/cartolasPOST /finanzas/bank-accounts/{id}/cartolas/previewPATCH /finanzas/bank-accounts/{id}/opening-balanceGET /finanzas/cartola-formatsGET /finanzas/casesGET /finanzas/cases/{id}POST /finanzas/cases/{id}/confirmPOST /finanzas/cases/{id}/confirm-editedPOST /finanzas/cases/{id}/dismissPOST /finanzas/cases/confirm-bulkGET /finanzas/cases/countDELETE /finanzas/cases/no-document/{movement_id}GET /finanzas/cash-flowGET /finanzas/cash-flow/calendarPOST /finanzas/cash-flow/movesDELETE /finanzas/cash-flow/moves/{id}GET /finanzas/cash-flow/overdue-no-payment-seenPOST /finanzas/cash-flow/overdue-no-payment-seen/resolveGET /finanzas/cash-flow/recurring-itemsPOST /finanzas/cash-flow/recurring-itemsPATCH /finanzas/cash-flow/recurring-items/{id}POST /finanzas/cash-flow/recurring-items/{id}/endPOST /finanzas/cash-flow/rescuesDELETE /finanzas/cash-flow/rescues/{id}GET /finanzas/chipaxDELETE /finanzas/chipax/credentialsGET /finanzas/chipax/importsPOST /finanzas/chipax/importsGET /finanzas/chipax/imports/{id}GET /finanzas/chipax/shadowPOST /finanzas/chipax/shadowGET /finanzas/chipax/shadow/{month}GET /finanzas/chipax/shadow/{month}/exportGET /finanzas/chipax/shadow/{month}/itemsGET /finanzas/classificationsDELETE /finanzas/classifications/{type}/{id}GET /finanzas/classifications/{type}/{id}PUT /finanzas/classifications/{type}/{id}POST /finanzas/classifications/bulkGET /finanzas/connect-setupPOST /finanzas/connect-setup/onboardingPOST /finanzas/connect-setup/siiPOST /finanzas/connect-setup/sii/{id}/mandatoGET /finanzas/connect-setup/sii/{id}/mandato/documentPOST /finanzas/connect-setup/sii/{id}/mandato/revokeGET /finanzas/connect-setup/sociedad-lookupGET /finanzas/connectionsGET /finanzas/connections/{id}/clave-tributariaPOST /finanzas/connections/{id}/disconnectPOST /finanzas/connections/{id}/reconnectPOST /finanzas/connections/{id}/syncPOST /finanzas/connections/fintocPOST /finanzas/connections/fintoc/link-intentGET /finanzas/connections/syncPOST /finanzas/connections/syncGET /finanzas/contador-exportGET /finanzas/contador-export/manifestGET /finanzas/contrapartesGET /finanzas/contrapartes/{id}PATCH /finanzas/contrapartes/{id}POST /finanzas/contrapartes/{id}/aliasesDELETE /finanzas/contrapartes/{id}/aliases/{aliasId}POST /finanzas/contrapartes/{id}/mergeGET /finanzas/contrapartes/{id}/overviewPUT /finanzas/contrapartes/{id}/payment-termsGET /finanzas/contrapartes/{id}/timelineGET /finanzas/contrapartes/duplicatesPOST /finanzas/contrapartes/duplicates/dismissPOST /finanzas/contrapartes/duplicates/mergeGET /finanzas/contrapartes/mergesPOST /finanzas/contrapartes/merges/{mergeId}/undoGET /finanzas/costosGET /finanzas/costos/settingsPATCH /finanzas/costos/settingsGET /finanzas/digest/previewGET /finanzas/documentsGET /finanzas/documents/{id}PATCH /finanzas/documents/{id}POST /finanzas/documents/{id}/reopenGET /finanzas/documents/f29GET /finanzas/documents/honorarios/summaryGET /finanzas/documents/libroGET /finanzas/documents/libro/exportGET /finanzas/documents/uploadsPOST /finanzas/documents/uploadsDELETE /finanzas/documents/uploads/{id}GET /finanzas/documents/uploads/{id}POST /finanzas/documents/uploads/{id}/confirmPOST /finanzas/expenses/from-movementsGET /finanzas/howto-videosGET /finanzas/inboxPOST /finanzas/inbox/rotateGET /finanzas/inherited-overdueGET /finanzas/inherited-overdue/batchesPOST /finanzas/inherited-overdue/batches/{batchId}/undoPOST /finanzas/inherited-overdue/resolveGET /finanzas/monthsGET /finanzas/months/{month}GET /finanzas/months/{month}/adjustmentsPOST /finanzas/months/{month}/closeGET /finanzas/movementsGET /finanzas/movements/{id}GET /finanzas/movements/{id}/commentsPOST /finanzas/movements/{id}/commentsGET /finanzas/own-account-suggestionsPOST /finanzas/own-account-suggestions/{key}/acceptPOST /finanzas/own-account-suggestions/{key}/dismissGET /finanzas/payment-termsPATCH /finanzas/payment-termsGET /finanzas/possible-duplicatesGET /finanzas/produccionGET /finanzas/reconciliation/cash-pendingGET /finanzas/reconciliation/commissionsGET /finanzas/reconciliation/documents/{id}POST /finanzas/reconciliation/linksPOST /finanzas/reconciliation/links/{id}/undoGET /finanzas/reconciliation/movements/{id}GET /finanzas/reconciliation/movements/{id}/candidatesPOST /finanzas/reconciliation/runGET /finanzas/resultadoGET /finanzas/resultado/exportGET /finanzas/resultado/itemsGET /finanzas/resumenGET /finanzas/rulesPOST /finanzas/rulesGET /finanzas/rules/{id}PATCH /finanzas/rules/{id}POST /finanzas/rules/{id}/applyPOST /finanzas/rules/{id}/archivePOST /finanzas/rules/{id}/pauseGET /finanzas/rules/{id}/pendingPOST /finanzas/rules/{id}/resumeGET /ops/finanzas-howto-videosDELETE /ops/finanzas-howto-videos/{screen}PUT /ops/finanzas-howto-videos/{screen}