Ir al contenido
docs

Changelog

Cambios de la API pública de Posdata, versión por versión.

Aquí anunciamos cada cambio de la API y de los webhooks. Los cambios que rompan la compatibilidad (quitar o renombrar un campo, un código de error, un permiso o un tipo de evento) no ocurren sin una nueva versión y un aviso previo. Agregar campos, endpoints, filtros o tipos de evento no se considera un cambio que rompe: tu integración debe tolerar campos nuevos.

v1.0.0 — actualización — 2026-10-20 (compras a proveedores)

Las compras a proveedores. La API no tiene endpoints de proveedores ni de compras: el negocio las registra desde la app y las ves como movimientos de stock. Detalle en Cómo funciona el stock.

  • origin_type: "purchase" en el movimiento de stock (GET /inventory/movements y el webhook inventory.movement.created): la entrada de una compra recibida. Sube el costo promedio con el costo de la compra (precio menos descuento, más flete y el impuesto que el negocio no descuenta).
  • origin_type: "purchase_return": una salida de mercancía devuelta al proveedor. Sale al costo promedio.
  • origin_type: "reversal" también cubre ahora la anulación de una compra (una salida que deshace la entrada), además de la reposición de una venta anulada (una entrada).
  • Son valores nuevos de un campo que ya existe: si tu integración ramifica por origin_type, trátalos como casos más. No cambia ningún campo ni endpoint.

v1.0.0 — actualización — 2026-10-20 (traslados entre bodegas)

Los traslados entre bodegas con mercancía en tránsito. La API no tiene endpoints de traslados: los ves en los movimientos, el kardex y el valor del inventario. Detalle en Cómo funciona el stock.

  • origin_type: "transfer" en el movimiento de stock (GET /inventory/movements y el webhook inventory.movement.created): una salida en la bodega de origen al despachar y una entrada en la de destino al recibir (o en la de origen, si se devuelve). Un traslado no cambia el costo promedio. Es un valor nuevo de un campo que ya existe: si tu integración ramifica por origin_type, trátalo como un caso más.
  • transfer en las filas del kardex (campo nuevo, null si la fila no es de un traslado): { id, number, kind, counterpart }, con el número del traslado y la bodega del otro extremo.
  • in_transit_value ahora es real en GET /inventory/valuation (antes siempre 0): el valor de la mercancía despachada y aún no recibida, aparte de total_value (no está en ningún nivel). Con warehouse_id cuenta lo que viene hacia esa bodega.
  • in_transit en GET /inventory/valuation (campo nuevo): el detalle por línea. Una línea sin costo conocido viene con unit_cost y value en null y se cuenta en missing_cost_lines; nunca se valora en 0.
  • Todos los cambios son aditivos: tu integración debe tolerar campos y valores nuevos.

v1.0.0 — actualización — 2026-10-20

Costos, kardex y valor del inventario. Todo el detalle en Kardex y costo promedio y Valor del inventario.

  • unit_cost en POST /inventory/movements: una entrada (type: "in") puede declarar lo que costó cada unidad base. Alimenta el costo promedio ponderado móvil de la cuenta (uno por producto). Es opcional y debe ser mayor que 0: si no lo conoces, no lo envíes. Con type: "out" responde 400 VALIDATION_ERROR; una salida sale siempre al costo promedio.
  • GET /inventory/kardex (permiso inventory:read): el libro de un producto, con cada movimiento, conteo y ajuste de costo en orden y el saldo corrido. Filtros warehouse_id, variant_id, from, to, type y order. Pagina con page y page_size (máximo 200), no con cursor. Con la cuenta en legacy responde 409 STOCK_MODE_LEGACY.
  • GET /inventory/valuation (permiso inventory:costs): el valor del inventario hoy o a una fecha de corte (at), por bodega. Los niveles negativos y los productos sin costo no suman: vienen aparte en negatives y missing_cost, nunca valorados en 0. Incluye computed_at.
  • Nuevo permiso inventory:costs: ver costos y valores del inventario. Se pide aparte de inventory:read: con solo inventory:read el kardex trae cantidades y saldos y todo costo y valor viene en null (costs_hidden: true). Las llaves existentes no lo tienen; créalo en una llave nueva (cuenta.posdata.so → Desarrolladores) solo si lo necesitas.
  • reason_code en el movimiento de stock (campo nuevo, null si no aplica): el motivo catalogado de un ajuste hecho en la app (shrinkage, damage, expired, theft, internal_use, supplier_return, found, correction, other o transfer_shortage). También llega en data del webhook inventory.movement.created.
  • Todos los cambios son aditivos: tu integración debe tolerar campos nuevos en el movimiento y en el webhook.

v1.0.0 — actualización — 2026-10-14

Packs y combos como producto.

  • kind: "kit" en los productos: un pack o combo agrupa otros productos bajo un precio propio (un six-pack, un kit escolar, un combo de restaurante) y, al venderse, descuenta el stock de cada producto que contiene. El campo kind del producto ahora puede ser simple, composite o kit. Es un cambio aditivo en la respuesta: si tu integración ramifica por kind, trata el valor nuevo como un caso más, no como un error.
  • stock es null en un pack, igual que en un producto que se arma con receta: el stock lo tienen los productos que contiene.
  • Mover o contar el stock de un pack (POST /inventory/movements y POST /inventory/counts) responde 422 COMPOSITE_STOCK_UNSUPPORTED, como con las recetas: mueve o cuenta el stock de los productos que contiene.

v1.0.0 — actualización — 2026-09-29

El stock oficial por bodega. Todo el detalle en Cómo funciona el stock.

  • stock publicado en las cuentas que ya llevan el stock en la nube (todas sus cajas principales con la versión de Posdata que lo soporta; el paso es automático): el stock de un producto es la suma de sus niveles y cada variante trae su propio stock (campo nuevo). Mientras la cuenta no haya pasado, stock sigue siendo null. Ahora es un number (antes integer): puede tener decimales en productos por peso y ser negativo.
  • GET /inventory/levels: el stock por (bodega, producto, variante), con available (el stock menos el margen de la API) y el último conteo. Filtros product_id, variant_id y warehouse_id.
  • POST /inventory/counts: registra un conteo físico en el instante en que se hizo (counted_at); las ventas posteriores se suman encima. counted_quantity es entero, salvo en productos que se venden por peso. Permiso inventory:write e Idempotency-Key.
  • POST /inventory/movements: con el stock en la nube, una salida out es atómica y nunca compromete las últimas unidades que el negocio reserva para sus cajas (el margen de la API, 2 por defecto, se ajusta en cuenta.posdata.so → Inventario → Configuración). Si no alcanza, responde el nuevo código 409 INSUFFICIENT_STOCK con available y no registra nada; antes, la API no verificaba existencias. Ahora acepta variantes (variant_id) y quantity con decimales en productos que se venden por peso.
  • Nuevo código STOCK_MODE_LEGACY (409): lo responden los niveles, los conteos y los movimientos de variantes mientras la cuenta no lleve el stock en la nube.
  • VARIANT_STOCK_UNSUPPORTED queda obsoleto y ya no se emite: los movimientos de variantes se aceptan cuando la cuenta lleva el stock en la nube; antes de eso responden 409 STOCK_MODE_LEGACY. El código se conserva en el catálogo hasta una nueva versión de la API. Si tu integración ramificaba por él, trata STOCK_MODE_LEGACY de la misma forma.
  • Permisos: inventory:read ahora incluye el stock por bodega e inventory:write, los conteos.
  • Webhooks nuevos: inventory.count.created (un conteo físico, desde una caja o por la API) e inventory.stock.negative (un nivel contado quedó negativo porque una caja vendió sin internet lo que ya no había).

v1.0.0 — actualización — 2026-09-28

  • Editar productos y variantes: PATCH /products/{id} (nombre, descripciones, SKU, categoría, precios, clase fiscal, estado) y PATCH /products/{id}/variants/{variantId} (etiqueta, SKU, precios, estado). El cambio llega a las cajas sin stock: cada caja conserva el suyo. Solo funciona cuando todos los dispositivos del negocio tienen la versión de Posdata que lo soporta; si no, responde el nuevo código 409 DEVICES_OUTDATED con la lista de dispositivos y no modifica nada. El permiso products:write ahora incluye estas ediciones, y emiten product.updated. Detalle en Productos: se crean y se editan.
  • occurred_at en las ventas: cuándo se registró la venta en la caja (en ventas sincronizadas antes del 28-sep-2026, igual a la hora de llegada). Nuevos filtros occurred_since y occurred_until en GET /sales, y el reporte GET /reports/daily-sales ahora agrupa por occurred_at. created_at sigue siendo la hora de llegada a la nube. Mira Las ventas traen dos fechas.
  • Búsqueda literal: en q, los caracteres % y _ se buscan tal cual, no como comodines.
  • stock en productos: el campo entró al contrato, todavía en null. Se publicó con la actualización del 2026-09-29 (arriba).

v1.0.0 — 2026-09-28

Primera versión pública.

  • API REST en https://api.posdata.so/public/v1 (pruebas: https://apidev.posdata.so/public/v1), incluida en el plan Pro.
  • Autenticación con API keys de cuenta (pdk_live_/pdk_test_) y 12 permisos de mínimo privilegio.
  • Lectura de cuenta y frescura de datos (/me), productos y variantes, categorías, clientes, bodegas, movimientos de inventario, ventas, documentos electrónicos, cotizaciones, turnos de caja, ventas por día y registro de eventos.
  • Escritura: crear y editar categorías y clientes, crear productos sin variantes (con stock inicial opcional) y registrar movimientos de inventario de productos sin variantes. Todo POST exige Idempotency-Key. La edición de productos y variantes llegó con la actualización del mismo día (arriba).
  • Webhooks firmados con HMAC-SHA256 (api_version 2026-09-28) para 12 tipos de evento, con reintentos durante unas 28 horas y reconciliación con GET /events.
  • Límites: 300 peticiones por minuto por key, 600 por cuenta y 60 escrituras por minuto por key.

Limitaciones conocidas de esta versión: Lo que la API no hace en la versión 1.