Finanzas
The business's back-office money, for any vertical: connected banks, Mercado Pago and SII, with their status and last sync.
Download the full API pública projection: openapi.json.
| Method | Path | What it does |
|---|---|---|
GET | /finanzas/accounts | Plan de cuentas: the account tree |
POST | /finanzas/accounts | Add an account |
DELETE | /finanzas/accounts/{id} | Delete an unused account |
PATCH | /finanzas/accounts/{id} | Rename, move or retag an account |
POST | /finanzas/accounts/{id}/archive | Archive an account |
POST | /finanzas/accounts/{id}/restore | Restore an archived account |
GET | /finanzas/accounts/review | Plan de cuentas: the monthly review |
POST | /finanzas/accounts/review/dismiss | Plan de cuentas: «No son la misma» |
GET | /finanzas/ads-charges | Meta billing charges synced, with their link state |
POST | /finanzas/ads-charges/sync | Read the ad data now and match the card charges |
GET | /finanzas/ads-connections | Ad accounts reachable for Cargos de publicidad |
GET | /finanzas/ai/metrics | IA metrics of a month: calls, auto-applied, undo rate |
GET | /finanzas/ai/settings | Read the IA switch (Aplicar sugerencias de IA automáticamente) |
PUT | /finanzas/ai/settings | Turn automatic IA suggestions on or off |
GET | /finanzas/alerts | Active Finanzas alerts |
GET | /finanzas/automatic-actions | Hecho por Vitrina: what Vitrina applied on its own in a month |
POST | /finanzas/automatic-actions/{kind}/{id}/undo | Undo an automatic action (Deshacer) |
POST | /finanzas/automatic-actions/review | Mark automatic actions reviewed (Marcar revisado) |
GET | /finanzas/bank-accounts | List bank accounts with balances (Cuentas) |
POST | /finanzas/bank-accounts | Create a manual bank or card account |
PUT | /finanzas/bank-accounts/{id}/agent-sharing | Share a bank account with the AI agent (or stop) |
DELETE | /finanzas/bank-accounts/{id}/cartola-mapping | Forget an account's saved column mapping |
GET | /finanzas/bank-accounts/{id}/cartolas | List the statements uploaded to an account |
POST | /finanzas/bank-accounts/{id}/cartolas | Import a statement into Movimientos |
POST | /finanzas/bank-accounts/{id}/cartolas/preview | Preview a statement before importing it |
PATCH | /finanzas/bank-accounts/{id}/opening-balance | Set a bank account's opening balance |
GET | /finanzas/cartola-formats | List the statement formats read with no setup |
GET | /finanzas/cases | The «Por resolver» inbox: open cases, biggest first |
GET | /finanzas/cases/{id} | One case with its proposal |
POST | /finanzas/cases/{id}/confirm | Confirm a case’s proposal («Resolver») |
POST | /finanzas/cases/{id}/confirm-edited | Confirm an edited proposal |
POST | /finanzas/cases/{id}/dismiss | Dismiss 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-flow | Flujo de caja: actual, projection, band and lowest point |
GET | /finanzas/cash-flow/calendar | Payments 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/resolve | Mark «Vencidas sin pago visto» as paid |
GET | /finanzas/cash-flow/recurring-items | Recurring items of the cash flow |
POST | /finanzas/cash-flow/recurring-items | Declare 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/chipax | Importar desde Chipax: connection and runs |
DELETE | /finanzas/chipax/credentials | Importar desde Chipax: «Eliminar credenciales» |
GET | /finanzas/chipax/imports | Importar desde Chipax: the runs |
POST | /finanzas/chipax/imports | Importar 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/shadow | Mes en paralelo: the months read from Chipax |
POST | /finanzas/chipax/shadow | Mes 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}/export | Mes en paralelo: download the diff (xlsx) |
GET | /finanzas/chipax/shadow/{month}/items | Mes en paralelo: the items behind a row |
GET | /finanzas/classifications | Classification 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/bulk | Classify many documents or movements to one account |
GET | /finanzas/connect-setup | What is connected and what comes next (bank, SII, honorarios) |
POST | /finanzas/connect-setup/onboarding | Onboarding state: start, defer, or «no recibe boletas de honorarios» |
POST | /finanzas/connect-setup/sii | Connect a razón social to the SII (step 1) |
POST | /finanzas/connect-setup/sii/{id}/mandato | Vitrina's mandato for the representante legal to sign (step 3) |
GET | /finanzas/connect-setup/sii/{id}/mandato/document | The signed mandato PDF |
POST | /finanzas/connect-setup/sii/{id}/mandato/revoke | Revoke the mandato (cláusula 7.1) |
GET | /finanzas/connect-setup/sociedad-lookup | The SII's public record of a RUT (razón social prefill) |
GET | /finanzas/connections | Money connections: banks, Mercado Pago and SII |
GET | /finanzas/connections/{id}/clave-tributaria | SII clave status for boletas de honorarios |
POST | /finanzas/connections/{id}/disconnect | Disconnect a bank account |
POST | /finanzas/connections/{id}/reconnect | Reconnect a bank account |
POST | /finanzas/connections/{id}/sync | Sync a connection now |
POST | /finanzas/connections/fintoc | Connect a bank account |
POST | /finanzas/connections/fintoc/link-intent | Start a bank link (Fintoc widget token) |
GET | /finanzas/connections/sync | Freshness of the bank movements |
POST | /finanzas/connections/sync | Sync every bank link |
GET | /finanzas/contador-export | Download the Contador package for a month (zip) |
GET | /finanzas/contador-export/manifest | What the Contador package for a month holds |
GET | /finanzas/contrapartes | List 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}/aliases | Link 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}/merge | Merge a duplicate Contraparte into this one |
GET | /finanzas/contrapartes/{id}/overview | The Contraparte page: balance, terms, linked accounts, rules |
PUT | /finanzas/contrapartes/{id}/payment-terms | Set a Contraparte's payment terms |
GET | /finanzas/contrapartes/{id}/timeline | A Contraparte's movements and documents, newest first |
GET | /finanzas/contrapartes/duplicates | Proposed Contraparte duplicates («Unir duplicados») |
POST | /finanzas/contrapartes/duplicates/dismiss | Reject a proposed duplicate («No son la misma») |
POST | /finanzas/contrapartes/duplicates/merge | Accept a proposed duplicate: merge it (undoable) |
GET | /finanzas/contrapartes/merges | Contrapartes merged in a month («Contrapartes unidas») |
POST | /finanzas/contrapartes/merges/{mergeId}/undo | Undo a Contraparte merge («Deshacer») |
GET | /finanzas/costos | Costos y equilibrio: fixed vs variable costs and the break-even |
GET | /finanzas/costos/settings | Read how Costos y equilibrio reads the dentist share |
PATCH | /finanzas/costos/settings | Change the dentist share accounts or the reference line |
GET | /finanzas/digest/preview | Preview the weekly Finanzas digest |
GET | /finanzas/documents | List 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}/reopen | Reopen a settled document («Reabrir») |
GET | /finanzas/documents/f29 | F29 by period, with the next due date |
GET | /finanzas/documents/honorarios/summary | Monthly honorarios and retención total |
GET | /finanzas/documents/libro | Monthly libro de compras or libro de ventas |
GET | /finanzas/documents/libro/export | Export the libro de compras or ventas (xlsx / csv) |
GET | /finanzas/documents/uploads | List uploaded and forwarded files |
POST | /finanzas/documents/uploads | Upload 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}/confirm | Confirm a file as a document |
POST | /finanzas/expenses/from-movements | Create gastos from many bank movements («Crear gasto») |
GET | /finanzas/howto-videos | «Cómo se usa» videos for the Finanzas screens |
GET | /finanzas/inbox | The workspace's forwarding address |
POST | /finanzas/inbox/rotate | Replace the forwarding address |
GET | /finanzas/inherited-overdue | Vencido heredado (retired): the payables to review |
GET | /finanzas/inherited-overdue/batches | Settle batches a person made |
POST | /finanzas/inherited-overdue/batches/{batchId}/undo | Undo a settle batch («Deshacer») |
POST | /finanzas/inherited-overdue/resolve | Vencido heredado (retired): mark pagado or anulado in bulk |
GET | /finanzas/months | Cierre 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}/adjustments | Ajustes posteriores al cierre of a month |
POST | /finanzas/months/{month}/close | Close a month (Cerrar mes) |
GET | /finanzas/movements | List bank and card movements (Movimientos) |
GET | /finanzas/movements/{id} | Get a movement with its comments |
GET | /finanzas/movements/{id}/comments | List a movement's comments |
POST | /finanzas/movements/{id}/comments | Comment on a movement |
GET | /finanzas/own-account-suggestions | Own-account cards: «Agregar ··XXXX como cuenta propia» |
POST | /finanzas/own-account-suggestions/{key}/accept | Add the suggested account as an own account |
POST | /finanzas/own-account-suggestions/{key}/dismiss | Hide the own-account card for that account («Ahora no») |
GET | /finanzas/payment-terms | Default payment terms per document class |
PATCH | /finanzas/payment-terms | Change default payment terms |
GET | /finanzas/possible-duplicates | Posibles duplicados: bank lines that repeat |
GET | /finanzas/produccion | Producción, cobrado y boleteado (clinics) |
GET | /finanzas/reconciliation/cash-pending | Efectivo por depositar |
GET | /finanzas/reconciliation/commissions | Payout commission lines |
GET | /finanzas/reconciliation/documents/{id} | A document's payments and candidates («Asignar pago») |
POST | /finanzas/reconciliation/links | Link a movement (Conciliar) |
POST | /finanzas/reconciliation/links/{id}/undo | Undo a link (Deshacer) |
GET | /finanzas/reconciliation/movements/{id} | A movement's reconciliation: links, payer, commission |
GET | /finanzas/reconciliation/movements/{id}/candidates | Candidates to reconcile a movement («Conciliar») |
POST | /finanzas/reconciliation/run | Run the reconciliation engine for a period |
GET | /finanzas/resultado | Resultado: the operating result by month |
GET | /finanzas/resultado/export | Export the resultado (xlsx / csv) |
GET | /finanzas/resultado/items | Resultado: the documents and movements behind a line |
GET | /finanzas/resumen | Resumen: the Finanzas home, three answers and the context |
GET | /finanzas/rules | Reglas: the classification rules |
POST | /finanzas/rules | Create 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}/archive | Archive a rule |
POST | /finanzas/rules/{id}/pause | Pause a rule |
GET | /finanzas/rules/{id}/pending | «Aplicar a pendientes»: preview |
POST | /finanzas/rules/{id}/resume | Resume a paused rule |
GET | /ops/finanzas-howto-videos | Manage «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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
include_archived | query | true \ | false | no |
usage_month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/accounts \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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."
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/accounts/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts/<id>/archive \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/accounts/<id>/restore \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/accounts/review \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
]
}
}Answers: 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"
}'Example response (200)
{
"data": {
"dismissed": true
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
curl https://api.vitrinadev.com/api/v1/finanzas/ads-charges \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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<customer id>) or Meta (FACEBK *<ref>, 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.
Body
| Field | Type | Required | Constraints |
|---|
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/ads-charges/sync \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Example response (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
}
}
}Answers: 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<customer id>) or Meta (FACEBK *<ref>, 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"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | query | string | yes | 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"Example response (200)
{
"data": {
"month": "2026-10",
"calls": 42,
"auto_applied": 18,
"proposed": 15,
"no_answer": 9,
"undone": 1,
"undo_rate": 0.0556,
"input_tokens": 37800
}
}Answers: 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"Example response (200)
{
"data": {
"auto_apply": true,
"updated_at": null,
"updated_by": null
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
auto_apply | boolean | yes |
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
}'Example response (200)
{
"data": {
"auto_apply": false,
"updated_at": "2026-10-30T21:10:00.000Z",
"updated_by": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4f01"
}
}Answers: 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"Example response (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"
}
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | query | string | yes | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
group | query | string | no | patrón ^(?:contraparte:[0-9a-f-]{36}|own_transfers|cash_deposits|clinic_payments|none)$ |
reviewed | query | all \ | unreviewed | no |
curl https://api.vitrinadev.com/api/v1/finanzas/automatic-actions \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
kind | path | links \ | classifications | yes |
id | path | uuid | yes | |
adjustment_reason | query | string | no | mí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"Example response (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
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
items | object[] | — | |
month | string | — | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
group | string | — | 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"
}'Example response (200)
{
"data": {
"reviewed": 12
}
}Answers: 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"Example response (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
}
}Answers: 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"
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartola-mapping \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/bank-accounts/<id>/cartolas \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
filename | string | yes | mín. 1, máx. 255 |
content | string | yes | mín. 1, máx. 35000000 |
content_encoding | utf8 \ | base64 | — |
content_type | string | — | máx. 200 |
mapping | object | null | — | |
save_mapping | boolean | — | |
accept_gap | boolean | — |
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)"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
filename | string | yes | mín. 1, máx. 255 |
content | string | yes | mín. 1, máx. 35000000 |
content_encoding | utf8 \ | base64 | — |
content_type | string | — | máx. 200 |
mapping | object | 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)"
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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
}
}Answers: 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"Example response (200)
{
"data": [
{
"key": "banco_de_chile",
"label": "Banco de Chile",
"bank": "Banco de Chile"
},
{
"key": "santander",
"label": "Santander",
"bank": "Banco Santander"
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
kind | query | reconcile \ | transfer \ | classify \ |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0, ≤ 100000 |
period | query | recent \ | history \ | all |
month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
order | query | amount \ | needs_person | no |
curl https://api.vitrinadev.com/api/v1/finanzas/cases \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/cases/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
fingerprint | string | yes | 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"
}'Example response (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": []
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
fingerprint | string | — | mín. 1, máx. 128 |
actions | any[] | yes |
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"
}
]
}'Example response (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": []
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/cases/<id>/dismiss \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
"next_case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
adjustment_reason | query | string | no | mí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"
}
]
}'Example response (200)
{
"data": {
"confirmed": [
{
"case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c01",
"applied": {
"links": 1,
"classifications": 0,
"accepted": 0
}
}
],
"failed": []
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/cases/count \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"open": 2,
"amount_at_stake_clp": 855290,
"by_kind": {
"reconcile": 1,
"transfer": 0,
"classify": 0,
"missing_document": 1,
"unknown": 0
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
movement_id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cases/no-document/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
"case_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4c02"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
horizon_days | query | integer | no | ≥ 1, ≤ 365 |
granularity | query | day \ | week | no |
curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
direction | query | inflow \ | outflow | no |
curl https://api.vitrinadev.com/api/v1/finanzas/cash-flow/calendar \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
dry_run | query | true \ | false | no |
horizon_days | query | integer | no | ≥ 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"
}'Example response (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"
}
}
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
horizon_days | query | integer | no | ≥ 1, ≤ 365 |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cash-flow/moves/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}
}
}Answers: 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"Example response (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
}
]
}
}Answers: 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ú"
}'Example response (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
}
}
}Answers: 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"Example response (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"
}
]
}Answers: 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"
}'Example response (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"
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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
}'Example response (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"
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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"
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
dry_run | query | true \ | false | no |
horizon_days | query | integer | no | ≥ 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ú"
}'Example response (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"
}
}
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/cash-flow/rescues/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}
}Answers: 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"Example response (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
}
}
}Answers: 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"Example response (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
}
}
}Answers: 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"Example response (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
}
]
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
mode | dry_run \ | import | yes |
from | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
to | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
account_map | object[] | — |
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"
}
]
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/imports/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}
}Answers: 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"Example response (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"
}
]
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
month | string | — | 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"
}'Example response (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"
}
}Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | 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"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | 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"Answers: 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:<line> 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
dimension | query | balances \ | payables \ | costs \ |
key | query | string | no | máx. 200 |
status | query | differences \ | all | no |
limit | query | integer | no | ≥ 1, ≤ 500 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/chipax/shadow/<id>/items \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
type | query | FinanzasItemType | yes | |
ids | query | string | yes | mín. 1 |
curl https://api.vitrinadev.com/api/v1/finanzas/classifications \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
type | path | documents \ | movements | yes |
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/classifications/<id>/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
type | path | documents \ | movements | yes |
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/classifications/<id>/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
type | path | documents \ | movements | yes |
id | path | uuid | yes | |
adjustment_reason | query | string | no | mí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"
}
]
}'Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
adjustment_reason | query | string | no | mí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"
}'Example response (200)
{
"data": {
"classified": 2,
"items": [
{
"type": "movement",
"id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01"
},
{
"type": "movement",
"id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
refresh | query | 0 \ | 1 | no |
connection_id | query | uuid | no |
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
action | start \ | 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"
}'Example response (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
}
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
legal_entity_id | uuid | — | |
legal_entity | object | — | |
resolucion | object | — |
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"
}'Example response (201)
{
"data": {
"connection_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b01",
"legal_entity_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5b02",
"created_legal_entity": false
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
signer | object | — | |
send_email | boolean | — |
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
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
file | query | signed \ | certificate | no |
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii/<id>/mandato/document \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sii/<id>/mandato/revoke \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
rut | query | string | yes | mín. 3, máx. 20 |
curl https://api.vitrinadev.com/api/v1/finanzas/connect-setup/sociedad-lookup \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"source": "sii",
"rut": "76123456-7",
"legal_name": "CLINICA EJEMPLO SPA",
"giro": "SERVICIOS ODONTOLOGICOS",
"address_street": null,
"comuna_code": null,
"comuna_name": null
}
}Answers: 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"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/clave-tributaria \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/disconnect \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/connections/<id>/sync \
-H "Authorization: Bearer $VITRINA_KEY"Example response (202)
{
"data": {
"enqueued": true,
"next_refresh_at": "2026-10-30T09:00:00.000Z"
}
}Answers: 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"
}'Example response (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
}
}
}Answers: 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"Example response (201)
{
"data": {
"widget_token": "widget_token_ejemplo_123",
"external_id": "li_ejemplo123"
}
}Answers: 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"Example response (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
}
}Answers: 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"Example response (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
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /finanzas/contador-export
Download the Contador package for a month (zip)
One zip, paquete-contador-<period>.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 <name>-<period>.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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/contador-export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patró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"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
q | query | string | no | máx. 200 |
role | query | supplier \ | customer \ | both |
sort | query | name \ | activity | no |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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."
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
aliasId | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/aliases/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/overview \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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
}'Example response (200)
{
"data": {
"terms": {
"days": 45,
"by_class": [
{
"class": "suppliers",
"label": "Proveedores",
"days": 45,
"source": "contraparte",
"origin": "manual"
}
]
},
"recomputed_documents": 3
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
type | query | document \ | movement | no |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/contrapartes/<id>/timeline \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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"Example response (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
}
}
]
}
}Answers: 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"
}'Example response (200)
{
"data": {
"a_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a5b",
"b_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a9e",
"dismissed": true
}
}Answers: 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"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | query | string | yes | 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"Example response (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
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
mergeId | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/contrapartes/merges/<id>/undo \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/costos \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}
}Answers: 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"Example response (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"
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
reference_share_enabled | boolean | — | |
reference_share_bps | integer | — | ≥ 1, ≤ 10000 |
professional_account_ids | array | null | — | |
chairs_override | integer | null | — | ≥ 1, ≤ 200 |
lunch_break_minutes | integer | 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
}'Example response (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"
}
}Answers: 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"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
kind | query | purchase \ | sale \ | honorario \ |
source | query | sii \ | vitrina \ | manual \ |
origin | query | ads | no | |
period | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
contraparte_id | query | uuid | no | |
payment_state | query | por_pagar \ | parcial \ | pagado \ |
paid_before_vitrina | query | true \ | false | no |
dte_type | query | integer | no | ≥ 1, ≤ 999 |
q | query | string | no | máx. 200 |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/documents \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
total_clp | integer | — | > 0 |
issue_date | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
issuer_rut | string | null | — | máx. 20 |
issuer_name | string | null | — | máx. 200 |
contraparte_id | string | null | — | |
dte_type | integer | null | — | ≥ 1, ≤ 999 |
folio | string | null | — | patrón ^\d{1,12}$ |
description | string | 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"
}'Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/documents/<id>/reopen \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/f29 \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
period | query | string | no | patró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"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
kind | query | purchase \ | sale | yes |
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/libro \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
kind | query | purchase \ | sale | yes |
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
format | query | xlsx \ | csv | no |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/libro/export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
status | query | pending_review \ | confirmed \ | discarded \ |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/uploads \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
filename | string | yes | mín. 1, máx. 255 |
content | string | yes | mín. 1, máx. 35000000 |
content_encoding | utf8 \ | base64 | — |
content_type | string | — | máx. 200 |
movement_id | uuid | — | |
case_id | uuid | — |
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"
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/finanzas/documents/uploads/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/documents/uploads/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
adjustment_reason | query | string | no | mín. 3, máx. 500 |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
total_clp | integer | — | > 0 |
issue_date | string | — | patrón ^\d{4}-\d{2}-\d{2}$ |
issuer_rut | string | null | — | máx. 20 |
issuer_name | string | null | — | máx. 200 |
contraparte_id | string | null | — | |
dte_type | integer | null | — | ≥ 1, ≤ 999 |
folio | string | null | — | patrón ^\d{1,12}$ |
description | string | null | — | máx. 300 |
net_clp | integer | null | — | ≥ 0 |
iva_clp | integer | null | — | ≥ 0 |
document_id | string | 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"
}'Example response (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": {}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
adjustment_reason | query | string | no | mí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"
}'Example response (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"
}
]
}
}Answers: 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"Example response (200)
{
"data": {
"videos": [
{
"screen": "resumen",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"embed_url": "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
}
]
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /finanzas/inbox
The workspace's forwarding address
The address to forward receipts and invoices to (finanzas+<token>@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"Example response (200)
{
"data": {
"address": "[email protected]",
"accepts_from": "workspace_members",
"created_at": "2026-10-30T15:00:00.000Z",
"rotated_at": null
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/inbox/rotate \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
contraparte | query | uuid | no | |
sort | query | due_date \ | amount | no |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
limit | query | integer | no | ≥ 1, ≤ 100 |
curl https://api.vitrinadev.com/api/v1/finanzas/inherited-overdue/batches \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
batchId | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
document_ids | uuid[] | — |
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 '{}'Example response (200)
{
"data": {
"batch_id": "01930000-0000-7000-8000-0000000000b9",
"resolution": "pagado",
"undone": [
"01930000-0000-7000-8000-0000000000d1"
],
"skipped": [],
"remaining": {
"count": 1135,
"amount_clp": 437600000
}
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
document_ids | uuid[] | yes | |
resolution | pagado \ | anulado | yes |
note | string | — | 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"
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/months \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | 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"Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/months/<id>/adjustments \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
month | path | string | yes | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
unreviewed_count | integer | yes | ≥ 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
}'Example response (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
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
bank_account_id | query | uuid | no | |
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
direction | query | inbound \ | outbound | no |
state | query | open \ | partial \ | reconciled |
amount_min | query | integer | null | no | ≥ 0, ≤ 9007199254740991 |
amount_max | query | integer | null | no | ≥ 0, ≤ 9007199254740991 |
q | query | string | no | máx. 200 |
contraparte_id | query | uuid | no | |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/movements \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/movements/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/movements/<id>/comments \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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."
}'Example response (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"
}
}Answers: 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"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
key | path | string | yes | 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 '{}'Example response (201)
{
"data": {
"id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4b01",
"name": "Cuenta ··4521"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
key | path | string | yes | 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"Example response (200)
{
"data": {
"key": "4521",
"dismissed": true
}
}Answers: 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"Example response (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
}
]
}Answers: 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
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
min_repeats | query | integer | no | ≥ 2, ≤ 10 |
curl https://api.vitrinadev.com/api/v1/finanzas/possible-duplicates \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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%"
}
]
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
limit | query | integer | no | ≥ 1, ≤ 200 |
per_month | query | integer | no | ≥ 1, ≤ 50 |
sin_boleta_month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/produccion \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | no | patrón ^\d{4}-\d{2}-\d{2}$ |
only_open | query | true \ | false | no |
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/cash-pending \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | yes | patrón ^\d{4}-\d{2}-\d{2}$ |
to | query | string | yes | patrón ^\d{4}-\d{2}-\d{2}$ |
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/commissions \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
q | query | string | no | máx. 200 |
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/documents/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
adjustment_reason | query | string | no | mín. 3, máx. 500 |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
movement_id | uuid | yes | |
links | any[] | yes | |
note | string | 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."
}'Example response (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
}
}Answers: 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:<id> 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | string | yes | 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_reason | query | string | no | mín. 3, máx. 500 |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/reconciliation/links/<id>/undo \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"movement_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4a10",
"document_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f4d01"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/movements/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes | |
q | query | string | no | máx. 200 |
curl https://api.vitrinadev.com/api/v1/finanzas/reconciliation/movements/<id>/candidates \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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": []
}
}Answers: 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.
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
from | string | yes | patrón ^\d{4}-\d{2}-\d{2}$ |
to | string | yes | 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"
}'Example response (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
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
curl https://api.vitrinadev.com/api/v1/finanzas/resultado \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
format | query | xlsx \ | csv | no |
curl https://api.vitrinadev.com/api/v1/finanzas/resultado/export \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
row | query | account \ | ingresos_sin_clasificar \ | egresos_sin_documento \ |
account_id | query | uuid | no | |
month | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
from | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
to | query | string | no | patrón ^\d{4}-(0[1-9]|1[0-2])$ |
limit | query | integer | no | ≥ 1, ≤ 200 |
offset | query | integer | null | no | ≥ 0 |
curl https://api.vitrinadev.com/api/v1/finanzas/resultado/items \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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
}
}
]
}
}Answers: 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"Example response (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
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
status | query | active \ | paused \ | archived \ |
curl https://api.vitrinadev.com/api/v1/finanzas/rules \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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"
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/rules/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
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
}'Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/apply \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": {
"rule_id": "0190a3c2-5d1e-7b6a-9f20-4c1d2e3f5c01",
"applied": 4
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/archive \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/pause \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/pending \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X POST https://api.vitrinadev.com/api/v1/finanzas/rules/<id>/resume \
-H "Authorization: Bearer $VITRINA_KEY"Example response (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"
}
}Answers: 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"Example response (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"
]
}
}Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
screen | path | FinanzasHowtoScreen | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/ops/finanzas-howto-videos/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 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.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
screen | path | FinanzasHowtoScreen | yes |
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"
}'Example response (200)
{
"data": {
"screen": "resumen",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"embed_url": "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ",
"label": "Resumen"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429