Modelo de datos
Cómo están armados los objetos de la API y las decisiones que conviene conocer antes de mapearlos a tu sistema.
Convenciones
- Los campos van en inglés y
snake_case. Cada objeto dice qué es enobject. - Todo campo del esquema viene siempre: si no hay dato, va
null(nunca se omite). La única excepción escustomeren la venta, que viene sólo coninclude=customer. - Fechas ISO 8601 con el huso de la sucursal y milisegundos:
2026-10-27T09:15:02.123-03:00. Las de los clientes, con el huso de la casa matriz. - Importes como número con hasta 2 decimales, en la moneda de la sucursal (
currency, ISO 4217). - En la v1 sólo se agregan campos y valores: ignorá lo que no conozcas y aceptá valores nuevos en los enumerados.
Cuenta y sucursales
La cuenta es la casa matriz de una cadena (o el local, si es uno solo). Una credencial ve la matriz y todas sus sucursales de hoy; cada objeto trae su branch_id. La matriz sale con is_headquarters: true. El external_code de una sucursal es el código que le pone el comercio, para cruzarla con tu sistema.
- Una sucursal que entra a la cadena trae su historia con su
updated_atde siempre (viejo): la sincronización incremental no la ve. ComparáGET /branchesen cada corrida y, para una nueva, hacé una carga inicial conbranch_id=<nueva>y una fecha vieja. - Una sucursal que sale de la cadena desaparece de todos los listados, sin lápida. Lo que ya sincronizaste de ella queda en tu base: decidí vos qué hacer. En la v1
activede una sucursal es siempretrue.
Una venta es una orden
Cada orden de Kicket es una venta: lo que se cobra en el mostrador, lo que pide una mesa, un pedido de la carta online, uno de delivery. En una mesa, cada ronda es una venta y check_id las agrupa: es la cuenta de la mesa.
Estado
| status | Qué es |
|---|---|
OPEN | Una ronda de mesa todavía sin cobrar. No tiene pagos. |
CLOSED | Cobrada (también la cerrada en $0, la fiada y la que nace pagada). Una devolución parcial la deja CLOSED, con el total achicado. |
CANCELED | Anulada sin devolver plata. |
REFUNDED | Anulada con la plata devuelta. |
Canal
| channel | Qué es |
|---|---|
counter | Mostrador: la caja. |
table | Mesa atendida por un mozo, o que comandó la caja. |
qr_table | Mesa que pidió sola con el QR. |
online | La carta online: para retirar o con envío propio. |
delivery | Un pedido que entró por una app de delivery. |
kiosk | Tótem de autoservicio. |
other | Reservado para canales nuevos. |
Los canales son los mismos de Analítica: lo que suma un canal en la API es lo que suma ese canal en el Resumen.
La mesa
tablees el nombre o número de la mesa;check_id, la cuenta que agrupa sus rondas (un id opaco y estable).coversson los cubiertos de la mesa: vienen iguales en cada ronda que se cerró junta. Contalos una vez porcheck_id, nunca los sumes por venta.waiterycashierson personas del equipo: un id opaco (stf_…) y el nombre visible. Nunca el mail.
Si tu sistema cuenta una venta por mesa
En otros sistemas una mesa es una sola venta; en Kicket es una venta por ronda. Para verla como una: agrupá por check_id y sumá total, tip_total y los pagos; los covers, una sola vez.
- Mientras la ronda está
OPEN,check_idpuede venirnull(nace al cobrar en las mesas sin atención en mesa): usátablehasta que llegue. Si la mesa se cobra en dos cierres distintos, son doscheck_id. sales_countdel reporte diario cuenta rondas, no mesas: el ticket promedio por mesa lo calculás vos, con la agrupación de arriba.- Un cobro de mesa con tarjeta sale repartido en un pago por ronda (con el mismo
method_id): para cruzarlo con el posnet, agrupá porcheck_idymethod_id. La propina va entip_totalde cada venta, no en un pago.
Los renglones, en árbol
items viene en árbol: parent_item_id apunta al renglón padre. La plata vive en un solo nivel, así la suma de line_total de los renglones vigentes da el total.
| kind | Qué es | Plata |
|---|---|---|
product | Un producto vendido. | Su precio, sin los adicionales. |
modifier | Un adicional («Leche de almendras») o una opción («Tamaño: Grande») de su padre. | El adicional, su precio; la opción, 0. |
combo | Un combo vendido. Su product_id es el del combo. | El precio del combo entero. |
combo_component | Lo que trajo el combo, colgando de él. | 0 (la plata está en el combo). |
external_codees el código externo vigente del producto, leído al armar la respuesta: si lo cambiás después, las ventas viejas muestran el nuevo. Pero cambiarlo no mueve elupdated_atde las ventas: las que ya sincronizaste se quedan con el código de antes. Para cruzar, usáproduct_idcontra/products(ahí sí se mueveupdated_atcuando cambia el código).modifier_idsale del nombre del adicional u opción: es el mismo en toda la cadena y cambia si el local lo renombra. En la v1 no hay un catálogo de modificadores: los conocés por las ventas.- Un renglón anulado sale aparte con
voided: true, cuándo y por qué. Si de 3 cafés se anularon 2, hay un renglón vivo con 1 y uno anulado con 2. quantitytiene hasta 3 decimales; en lo que se vende al peso,uniteskg. Lo que se vende por litro también sale conkgen la v1: el número son litros.- Cada renglón cumple
line_total = unit_price × quantity − discount. Los centavos que no cierran (un precio que no da justo en centavos, lo pesado) van en el descuento del último renglón y, en la venta, como un descuentootherllamado «Redondeo» (de pocos centavos, puede ser negativo). Un descuento sin rótulo sale comoother«Ajuste». - En los descuentos,
benefites el premio del programa de puntos (Kickets) ycompanyel de una empresa, también el que se da por el dominio del mail. - Un combo anulado entero sale como
comboanulado en $0, con su valor en los componentes anulados (al revés que un combo vivo): así lo anulado nunca se cuenta dos veces. combo_namedice de qué combo salió un renglón, también en ventas viejas, de antes de que existiera el árbol, donde los componentes vienen comoproductsueltos.
[
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": null,
"product_id": "66b2e47a91c3d05f8e2a4b61",
"external_code": "CAF-010",
"modifier_id": null,
"combo_name": null,
"quantity": 1,
"unit": "unit",
"unit_price": 2800,
"discount": 280,
"line_total": 2520,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8c3",
"kind": "product",
"name": "Café con leche"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": "6720a3f1c9e2b4d7a1f3e8c3",
"product_id": null,
"external_code": null,
"modifier_id": "a3f9c2e1b7d04",
"combo_name": null,
"quantity": 1,
"unit": "unit",
"unit_price": 600,
"discount": 60,
"line_total": 540,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8c3:m:a3f9c2e1b7d04",
"kind": "modifier",
"name": "Leche de almendras"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": "6720a3f1c9e2b4d7a1f3e8c3",
"product_id": null,
"external_code": null,
"modifier_id": "o5e1d7c3a9b20",
"combo_name": null,
"quantity": 1,
"unit": "unit",
"unit_price": 0,
"discount": 0,
"line_total": 0,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8c3:m:o5e1d7c3a9b20",
"kind": "modifier",
"name": "Tamaño: Grande"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": null,
"product_id": "66b2e47a91c3d05f8e2a4b70",
"external_code": "CMB-002",
"modifier_id": null,
"combo_name": "Desayuno clásico",
"quantity": 1,
"unit": "unit",
"unit_price": 7500,
"discount": 750,
"line_total": 6750,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8d0",
"kind": "combo",
"name": "Desayuno clásico"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": "6720a3f1c9e2b4d7a1f3e8d0",
"product_id": "66b2e47a91c3d05f8e2a4b62",
"external_code": "PAN-003",
"modifier_id": null,
"combo_name": "Desayuno clásico",
"quantity": 1,
"unit": "unit",
"unit_price": 0,
"discount": 0,
"line_total": 0,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8c4",
"kind": "combo_component",
"name": "Medialunas (3)"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": "6720a3f1c9e2b4d7a1f3e8d0",
"product_id": "66b2e47a91c3d05f8e2a4b63",
"external_code": "BEB-021",
"modifier_id": null,
"combo_name": "Desayuno clásico",
"quantity": 1,
"unit": "unit",
"unit_price": 0,
"discount": 0,
"line_total": 0,
"voided": false,
"voided_at": null,
"void_reason": null,
"id": "6720a3f1c9e2b4d7a1f3e8c5",
"kind": "combo_component",
"name": "Jugo de naranja"
},
{
"object": "sale_item",
"sale_id": "6720a3f1c9e2b4d7a1f3e8c2",
"parent_item_id": null,
"product_id": "66b2e47a91c3d05f8e2a4b64",
"external_code": "COC-104",
"modifier_id": null,
"combo_name": null,
"quantity": 1,
"unit": "unit",
"unit_price": 4500,
"discount": 0,
"line_total": 4500,
"voided": true,
"voided_at": "2026-10-27T09:31:12.045-03:00",
"void_reason": "Lo pidió por error",
"id": "6720a3f1c9e2b4d7a1f3e8c6:v1",
"kind": "product",
"name": "Tostado de jamón y queso"
}
]Productos de la cadena
/products trae una fila por sucursal: el «Café con leche» de una cadena de siete locales son siete productos, cada uno con su product_id, su precio y su estado. chain_product_id los junta: es el mismo en las copias que la casa matriz vinculó: las que nacen del alta de la cadena y las que se juntan en Cadena → «Vínculos entre sucursales» (el código externo no las vincula).
- Para «el producto de la cadena», agrupá por
chain_product_id; si vienenull, el producto no está vinculado: usá suid. - La disponibilidad es por fila:
statusyactivede la copia de cada sucursal. barcodees el código de barras del producto (el que lee el lector de la caja): único por sucursal ynullsi no tiene. No es elexternal_code, que es el código de tu sistema.- Los renglones de las ventas traen el
product_idde la copia de la sucursal: para sumar por producto de la cadena, cruzalo contra/products.
Los pagos
Los pagos de una venta suman su total. Además de la plata que entró, hay créditos que cubren una parte; los distingue kind:
| kind | Qué es |
|---|---|
payment | Plata que entró, con un medio de pago. |
credit | Crédito de una empresa (beneficio corporativo). |
on_account | Cuenta corriente: se fió, no entró plata todavía. |
deposit | La seña de una reserva. |
ticket_credit | El crédito de consumo que traía una entrada. |
table_credit | El crédito de una mesa VIP. |
method_ides la clave estable del medio (la lista de abajo) ymethod_typesu familia:cash,debit,credit,transfer,qr,walletuother.amountes la parte del total que cubre;charged_amount, lo que de verdad se cobró con ese medio (en lo online, con servicio, propina y envío; en la caja y la mesa, la parte que se cargó, sin la propina;nullen los créditos).- Los pagos de una venta son un conjunto: cuando cambia el
updated_atde la venta, reemplazalos todos. Si se corrige el reparto, un pago puede cambiar de id. - Una devolución sale del pago por cuyo medio volvió la plata: si una venta pagada con efectivo y débito devuelve una parte en efectivo, sólo el pago en efectivo queda
partially_refunded.charged_amountes lo que se cobró con ese medio,refunded_amountlo que volvió yamountlo que quedó de la venta en ese medio. - En una venta
CANCELEDlos pagos salenapprovedsi la plata quedó en la caja, ycanceledsi nunca se cobró (una venta duplicada o cargada por error): ahí ningún pago suma.
Los medios de pago
| method_id | Nombre | type |
|---|---|---|
cash | Efectivo | cash |
card_debit | Tarjeta débito | debit |
card_credit | Tarjeta crédito | credit |
card_prepaid | Tarjeta prepaga | debit |
card | Tarjeta | other |
mercadopago | MercadoPago | wallet |
transfer | Transferencia | transfer |
mercadopago_qr | QR Mercado Pago | qr |
nave_qr | QR Nave | qr |
modo_qr | QR MODO | qr |
other_qr | QR de otro procesador | qr |
qr | QR | qr |
mp_point | MercadoPago Point | other |
nave_point | Terminal Nave | other |
getnet_point | Terminal Getnet | other |
clover | Terminal Clover | other |
online_mercadopago | Online · Mercado Pago | other |
online_nave | Online · Nave | other |
online_getnet | Online · Getnet | other |
online_stripe | Online · Stripe | other |
online_dlocalgo | Online · dLocal Go | other |
online_mobbex | Online · Mobbex | other |
online_fiserv | Online · Fiserv | other |
online_payway | Online · Payway | other |
online_modo | Online · MODO | other |
online_pvs | Online · PVS | other |
delivery_pedidosya | PedidosYa | other |
delivery_rappi | Rappi | other |
delivery_ubereats | Uber Eats | other |
delivery | Aplicación de delivery | other |
on_account | Cuenta corriente | other |
corporate_credit | Crédito corporativo | other |
reservation_deposit | Seña de reserva (online) | other |
ticket_credit | Crédito de la entrada | other |
table_credit | Crédito de la mesa | other |
other | Otro | other |
GET /payment-methods dice en qué sucursales está habilitado cada uno.
Los ids
Casi todos son el id de Kicket: 24 caracteres hexadecimales, únicos en toda la plataforma. Algunos objetos no tienen un id propio en Kicket y la API les arma uno estable:
| Qué | Forma | Ejemplo |
|---|---|---|
| Cliente | cus_ + 24 hex | cus_8f2a1c9d0e7b4a65c3d1e2f0 |
| Persona del equipo | stf_ + 24 hex | stf_1b9e5c2a7d4f8e03a6c2b9d1 |
| Combo vendido (renglón) | El id de esa instancia del combo | 6720a3f1c9e2b4d7a1f3e8d0 |
| Adicional u opción | <renglón>:m:<token> | 6720a3f1c9e2b4d7a1f3e8c3:m:a3f9c2e1b7d04 |
| Renglón anulado | <renglón>:v<n> | 6720a3f1c9e2b4d7a1f3e8c6:v1 |
| Pago sin id propio (ventas viejas, el cobro online) | <venta>:p<n> | 6720a3f1c9e2b4d7a1f3e8c2:p0 |
| Pago con un crédito | <venta>:<kind> | 6720a3f1c9e2b4d7a1f3e8c2:ticket_credit |
| Renglón de delivery sin producto vinculado (product_id null) | <venta>:u<n> | 6720a3f1c9e2b4d7a1f3e8c2:u0 |
| Cobro sin detalle de productos, o una diferencia que ningún renglón explica | <venta>:sin-detalle | 6720a3f1c9e2b4d7a1f3e8c2:sin-detalle |
| Medio de pago | Una clave | card_debit |
Qué queda afuera
- El consumo que viene incluido con una entrada y los vales de regalo: su plata está en la entrada, y Analítica tampoco los cuenta como ventas.
- Los checkouts que nadie pagó (un link de cobro sin pagar). Cuando se pagan, aparecen.
- Las cajas abiertas: sólo se exponen cerradas.
- Las sucursales que dejaron de ser parte de la cadena.
- Mails del equipo, notas internas y datos de pagadores: nunca salen.
Lo que la v1 no tiene (y por qué)
| Dato | En la v1 |
|---|---|
| El IVA por venta o renglón | No hay IVA por producto: sólo existe en la factura de ARCA. total es con IVA. |
| «Para llevar» | No se distingue: sale como counter u online. |
| La propina por pago | Sólo por venta (tip_total). En la caja y la mesa ningún pago la trae; en lo online viene dentro de charged_amount. |
| Precio de lista contra precio con promoción | unit_price ya trae la promoción aplicada. |
| Listas de precios | price del producto es el de la carta. |
| El DNI del cliente | No sale. |
| Lo contado por medio en el arqueo | Sólo el efectivo contado (counted_cash). |
| El cliente de una mesa por QR | Su customer_id suele venir null: la mesa no pide datos. |
| Subcategorías, entradas y eventos | No están en la v1. |
Desde cuándo vale cada dato de la historia
La carga inicial puede traer años de ventas, pero algunos datos se empezaron a guardar hace poco. Antes de esa fecha no es que no pasó: no se guardaba.
| Dato | Desde | Antes |
|---|---|---|
| Renglones anulados (voided) | 28-oct-2026 | Lo anulado no aparece. |
| Id propio de cada pago dividido | 28-oct-2026 | <venta>:p<n> |
| Dos combos iguales en una venta, como dos renglones | 28-oct-2026 | Un solo renglón con cantidad 2. |
| covers | 28-oct-2026 | null |
| closed_at de una venta del mostrador | 28-oct-2026 | Igual a opened_at (el alta). |
| closed_by de una caja | 24-sep-2026 | null |
Algunos nombres, para no confundirse
method_typedel pago es lo mismo quetypedel medio de pago.statusde la venta va en MAYÚSCULAS (CLOSED); los demás enumerados, en minúscula.- Un combo es un
productcontype: combo;is_combodice lo mismo. expectedde una caja es lo vendido por medio, no lo esperado en el cajón (eso escounted_cash − cash_difference).- Lo que se vende por litro sale con
unit: kgy el número son litros.
Clientes
Un cliente es una persona en toda la cuenta: el mismo cus_… en todas las sucursales. Se identifica por su teléfono (en E.164) o, si no dejó, por su mail. La venta trae customer_id (o null si fue anónima); el cliente completo pide customers:read. El id es opaco: con él no se puede recuperar el teléfono ni el mail. Ver la referencia.
- Si alguien dejó primero sólo el mail y después el teléfono, son dos clientes en la v1: unirlos le cambiaría el id a uno que ya sincronizaste.
updated_atse mueve sólo cuando cambian sus datos (nombre, teléfono, mail, una sucursal nueva), no con cada compra:last_purchase_atcambia sin moverlo.GET /customers?phone=…y?email=…llevan el dato personal en la URL: no dejes esas URLs en logs que vea cualquiera. Lo que escribe el personal en el motivo de una anulación (void_reason) sale con los mails y teléfonos tapados.- Los clientes los arma Kicket cada minuto: el
customer_idde una venta recién cobrada puede llegar uno o dos minutos antes que el cliente a/customers(mientras tanto, suGETda 404 einclude=customerno lo trae).