Ir al contenido
docs

Cómo se sincroniza con las cajas

Qué ve la API, cuándo llegan tus cambios a las cajas y los límites de esta versión. Léelo antes de integrar.

Posdata funciona sin internet: cada caja (el computador, la tablet o el celular donde se vende) guarda sus datos localmente y los sube a la nube cuando tiene conexión. La API pública trabaja sobre esa nube. Entender esto te evita reportes equivocados.

La API ve lo que las cajas ya subieron

Cuando lees ventas, turnos de caja o movimientos, ves lo que las cajas ya subieron. Si una caja lleva horas sin internet, sus ventas de esas horas todavía no están en la API; aparecerán cuando se reconecte.

Para saber qué tan al día están los datos, consulta GET /me → data_freshness:

JSON
{
  "data_freshness": {
    "last_device_sync_at": "2026-09-28T14:58:12.000Z",
    "devices": [
      { "name": "Caja 01", "kind": "primary", "platform": "windows", "last_sync_at": "2026-09-28T14:58:12.000Z" },
      { "name": "Caja 02", "kind": "primary", "platform": "android", "last_sync_at": "2026-09-28T09:12:40.000Z" },
      { "name": "Tablet bodega", "kind": "primary", "platform": "android", "last_sync_at": null }
    ]
  }
}
  • last_sync_at es el último cambio que esa caja subió de verdad a la nube, no la última vez que se conectó. Es null si la caja nunca ha subido nada.
  • last_device_sync_at es el más reciente de todos.

En el ejemplo, la Caja 02 no sube nada desde las 9:12 (UTC). Puede que no haya vendido, o puede que esté sin internet con ventas pendientes: la API no puede distinguirlo. Antes de cerrar un reporte del día, revisa que las cajas que vendieron hayan subido cambios después de su cierre.

Las ventas traen dos fechas

Cada venta trae dos momentos distintos:

CampoQué es
occurred_atCuándo se registró la venta en la caja, con el reloj de esa caja. Es la fecha del negocio: úsala para reportes.
created_atCuándo la venta llegó a la nube. Una caja que vendió sin internet sube sus ventas al reconectarse, y quedan con la hora de subida.

En una caja con internet las dos fechas casi coinciden. Si la caja vendió el sábado sin conexión y subió el lunes, occurred_at dice sábado y created_at dice lunes.

Ventas sincronizadas antes del 28-sep-2026

En las ventas que llegaron a la nube antes del 28 de septiembre de 2026, occurred_at es igual a la hora de llegada (created_at): la hora de la caja no se guardaba.

Qué usa cada cosa:

  • occurred_since y occurred_until de GET /sales filtran por occurred_at (la fecha del negocio).
  • created_since, created_until y updated_since filtran por la llegada a la nube (y la última modificación). Son los filtros para la sincronización incremental: una venta que llega tarde tiene un created_at reciente, así que nunca te la saltas. Si sincronizas por occurred_since, una venta del sábado que sube el lunes cae en un rango que quizás ya procesaste.
  • El reporte GET /reports/daily-sales suma las ventas completadas por día calendario de Bogotá (America/Bogota) según occurred_at, con un rango de hasta 92 días. La venta del sábado cuenta el sábado, pero solo aparece en el reporte cuando la caja la sube: un día puede cambiar después de cerrado.

Tus cambios llegan a las cajas por su sincronización normal

Cuando creas algo por la API (un producto, una categoría, un cliente, un movimiento o un conteo de inventario) o editas un producto, una variante, una categoría o un cliente, el cambio queda en la nube de inmediato y las cajas lo reciben por su sincronización normal:

  • Cajas en línea: en segundos. Posdata les avisa que hay cambios nuevos.
  • Cajas sin conexión: cuando se reconecten.

La API responde en cuanto el cambio está guardado en la nube; no espera a que las cajas lo reciban. Para las cajas, tu integración es un dispositivo más del negocio. En las ediciones gana el cambio más reciente: si alguien edita el mismo cliente desde una caja, se queda el que llegue último.

Productos: se crean y se editan

  • Crear: POST /products crea productos sin variantes, con initial_stock opcional.
  • Editar un producto: PATCH /products/{id} cambia el nombre, las descripciones, el SKU, la categoría, los precios (base_price, final_price, percent_discount), la clase fiscal y el estado (active, sellable).
  • Editar una variante: PATCH /products/{id}/variants/{variantId} cambia su etiqueta, SKU, precios y estado (active). Responde el producto completo, con todas sus variantes.

Una edición nunca toca el inventario: el cambio viaja a las cajas sin stock, y cada caja conserva el suyo. Por eso tampoco se pueden editar el stock, la unidad (base_unit), la venta por peso (sold_by_weight) ni el tipo de producto (kind): cambiarían el significado del stock que ya tiene cada caja. Para cambiar existencias usa movimientos y conteos.

Si cambias base_price sin enviar final_price, final_price toma el mismo valor.

Las cajas deben estar actualizadas

Una caja con una versión vieja de Posdata interpretaría una edición sin stock como «stock en cero». Para que eso nunca pase, editar productos y variantes solo funciona cuando todos los dispositivos del negocio tienen la versión de Posdata que lo soporta. Si alguno no la tiene, la respuesta es 409 DEVICES_OUTDATED, no se modifica nada y el error trae la lista de dispositivos que faltan:

JSON
{
  "error": {
    "code": "DEVICES_OUTDATED",
    "message": "No se modificó nada: estas cajas todavía no tienen la versión de Posdata que acepta ediciones de productos desde la API: Caja 02. Ábrelas con internet para que se actualicen solas (tarda un minuto en reportarse) o, si ya no las usas, revócalas en cuenta.posdata.so → Dispositivos. Crear productos y mover stock sí funciona mientras tanto.",
    "devices": [
      {
        "name": "Caja 02",
        "kind": "primary",
        "platform": "android",
        "app_version": "260901-a1b2c3d",
        "last_seen_at": "2026-09-20T17:42:10.000Z"
      }
    ],
    "request_id": "req_…"
  }
}

Cómo resolverlo:

  1. Abre esos dispositivos con internet. Posdata se actualiza solo y, un minuto después, el dispositivo reporta la versión nueva. Luego reintenta la edición.
  2. Si ya no usas alguno, revócalo en cuenta.posdata.so → Dispositivos. Un dispositivo olvidado en un cajón también cuenta.

last_seen_at es la última vez que ese dispositivo se reportó (null si nunca lo hizo) y te ayuda a reconocer los que ya no se usan. Mientras tanto, crear productos y mover stock sí funciona.

Stock e inventario

El stock se lleva por nivel (bodega, producto, variante): el último conteo físico más los movimientos ocurridos después. Todo el detalle está en Cómo funciona el stock. Lo esencial para sincronizar:

  • La nube lleva el stock oficial cuando todas las cajas principales del negocio tienen la versión de Posdata que lo soporta (el paso es automático). Antes de eso, stock es null en productos y variantes, y GET /inventory/levels, POST /inventory/counts y los movimientos de variantes responden 409 STOCK_MODE_LEGACY. Mira los dos modos.
  • POST /inventory/movements suma (in) o resta (out) stock; nunca fija un valor absoluto. Con el stock en la nube, una salida es atómica y nunca compromete las últimas unidades que el negocio reserva para sus cajas (el margen de la API): si no alcanza, responde 409 INSUFFICIENT_STOCK y no registra nada.
  • POST /inventory/counts registra un conteo físico en el instante en que se hizo (counted_at): las ventas posteriores se suman encima, aunque lleguen más tarde.
  • Una entrada puede declarar su costo (unit_cost en POST /inventory/movements) y el negocio lleva un costo promedio por producto. GET /inventory/kardex te da el libro de un producto con su saldo corrido y GET /inventory/valuation, el valor del inventario hoy o a una fecha de corte; los costos y valores exigen el permiso inventory:costs y un costo desconocido es siempre null, nunca 0 (detalle).
  • Las ventas en las cajas nunca se rechazan. Si una caja sin internet vende lo que ya se comprometió por otro canal, el nivel puede quedar negativo: Posdata avisa al negocio para que cuente y envía el webhook inventory.stock.negative.
  • El stock que ves incluye solo lo que las cajas ya subieron, igual que las ventas: las ventas de una caja sin internet se aplican cuando se reconecta.

Dinero, impuestos y cantidades

  • Dinero: pesos colombianos (COP) en enteros, sin decimales ni centavos. 25900 son $25.900.
  • Precios de productos: tal como los digita el negocio. Lo normal es que incluyan impuestos; si el negocio configuró precios antes de impuestos, vienen así.
  • Tarifas de impuesto: porcentaje entero. 19 es 19 %, nunca 0.19. Los impuestos por unidad (como el de bolsas) traen per_unit y rate: null.
  • Impuestos de una venta: cada línea trae taxes congelados al momento de vender, y la venta trae tax_breakdown agrupado por tributo y tarifa, con el código DIAN (01 IVA, 04 INC…). La propina (tip_amount) nunca es base gravable. Ingreso neto = total - tax - tip_amount.
  • Cantidades: pueden tener decimales en productos que se venden por peso; se expresan en la unidad base del producto.
  • Fechas: ISO 8601 en UTC.

Lo que la API no hace en la versión 1

Para que sepas con qué contar hoy:

  • Publica el stock solo cuando la cuenta lo lleva en la nube: mientras alguna caja principal no tenga la versión de Posdata que lo soporta, stock es null y los niveles, los conteos y los movimientos de variantes responden 409 STOCK_MODE_LEGACY (detalle).
  • No edita el stock, la unidad, la venta por peso ni el tipo de un producto con PATCH (el stock cambia solo con movimientos y conteos), y solo edita productos y variantes cuando todos los dispositivos del negocio están actualizados (409 DEVICES_OUTDATED).
  • No mueve ni cuenta stock de productos con receta: muévelo en sus insumos.
  • No aparta stock: no hay reservas con vencimiento. Una salida descuenta de inmediato (o, con el stock en la nube, responde 409 INSUFFICIENT_STOCK si no alcanza), y no hay una operación que descuente varias líneas a la vez o ninguna.
  • El margen de la API es uno por cuenta, no por producto ni por bodega.
  • No calcula costos por su cuenta: el costo promedio sale de las entradas con unit_cost y de lo que el negocio registra en la app. El valor del inventario no incluye los productos sin costo ni los niveles negativos (los lista aparte).
  • No crea ventas, facturas ni pedidos externos, y no emite facturas electrónicas de ventas hechas fuera del POS.
  • No sube imágenes, no elimina registros y no crea variantes.
  • No publica el PDF ni el XML de los documentos electrónicos.
  • No ofrece OAuth para aplicaciones de terceros: cada negocio crea sus propias keys.
  • No restringe el acceso por dirección IP.

Si alguna de estas limitaciones te frena, cuéntanos tu caso en Soporte.