Crear el gasto de una lectura
Crea el gasto con lo leído, más tus correcciones en expense, con la factura adjunta. Un escaneo crea un gasto: repetirlo devuelve el mismo (200).
Autorización
apiKey expenses.writeLa llave del negocio, creada en el panel: Integraciones → API pública. vk_live_… es una llave real; vk_test_…, una llave de modo prueba (verificaciones simuladas, nada toca el libro real). Ver la guía «Modo de prueba».
Va en: header
Permiso: expenses.write
Parámetros de la ruta
Parámetros de consulta
Sobre qué negocio actúa la petición. Obligatorio solo si la llave entra a varios negocios; con uno solo, se deduce.
Parámetros de cabecera
Llave única de la operación. Un reintento con la misma llave y el mismo cuerpo devuelve la respuesta original en vez de volver a ejecutar. Ver la guía de idempotencia.
length <= 255Cuerpo de la petición
application/json
Tipos de TypeScript
Usa el tipo request body en TypeScript.
Cuerpo de la respuesta
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/expense-scans/cmuhb7c8d0061x8w1f2g3h4jk/expense" \ -H "Content-Type: application/json" \ -d '{ "expense": { "paidTo": "Harina La Espiga", "notes": "20 bultos de harina" } }'{ "object": "expense", "id": "cmtnox8lz00016kw11frj0rww", "amount": 120000, "currency": "COP", "paidTo": "Harina La Espiga", "occurredAt": "2026-09-04T14:10:00.000Z", "createdAt": "2026-09-04T14:12:44.008Z", "accountLabel": null, "paymentMethod": "TRANSFER", "notes": "Pedido semanal", "source": "API", "category": { "id": "cmt7xbfrm0003q3w121ldx6wz", "name": "Proveedores y mercancía", "emoji": "📦" }, "branch": null, "createdBy": { "id": null, "name": null, "phone": null }}Ver una lectura de factura GET
El escaneo, con los duplicados recalculados (o el gasto, si ya se creó).
Subir un extracto POST
Sube el extracto del banco y lo cruza contra el libro. La petición **espera la lectura hasta 20 s**: si alcanza, `201` con el extracto leído y cruzado; si no (un PDF escaneado, por ejemplo), `202` con `status: processing`, `Location` y `Retry-After: 10`, y la lectura sigue. Al terminar sale el webhook `bank_statement.processed` (o `bank_statement.failed`). `async: true` contesta `202` de una vez. «No cuadra con el resumen del banco» **no es un error**: el extracto se guarda con `verdict: MISMATCH`, `difference` y `checks`. No funciona con llaves de prueba (`403 TEST_MODE_UNAVAILABLE`).