Ir al contenido

Inventario externo

Con esta integración, el sistema de inventario del negocio (producción, bodega, compras) es la fuente de verdad de la existencia. papedir la muestra en su punto de venta y su tienda en línea, sigue descontando cada venta al instante y te avisa de cada pedido para que lo registres.

Tu sistema ──PUT /stock (absoluto)──▶ papedir
▲ │
└──── webhook order.created ─────────┘ (o GET /orders + ack)

Base: https://api.pedidos.transformit.com.co/api/v1/external. Autenticación y scopes en conceptos; errores en errores.

GET /catalog?cursor=&limit= (scope catalog:read)

Sección titulada «GET /catalog?cursor=&limit= (scope catalog:read)»

Devuelve los productos vivos del negocio en orden de id, entre 1 y 500 por página (100 por defecto). Para pedir la siguiente página se pasa next_cursor como cursor; cuando es null, no hay más.

{"items":[{"id":43,"sku":"PAN-01","external_id":"b3f…","name":"Pan de yuca","unit":"unidad",
"stock":12,"tracked":true,"available":true,"updated_at":"2026-10-08T15:00:00Z"}],
"next_cursor":43}

tracked: false significa que papedir no lleva inventario de ese producto: la existencia que se le mande se ignora (untracked). El negocio decide qué productos llevan inventario.

Hace el cruce inicial: le asigna a cada producto de papedir, buscado por su SKU, el id que tiene en tu sistema. Con null se quita el cruce. Acepta hasta 500 productos por petición.

Ventana de terminal
curl -s -X PATCH "$BASE/catalog/mapping" -H "Authorization: Bearer $LLAVE" \
-H 'Content-Type: application/json' \
-d '{"items":[{"sku":"PAN-01","external_id":"b3f1c2d4-…"},{"sku":"PAN-02","external_id":null}]}'
# {"results":[{"index":0,"sku":"PAN-01","status":"mapped","menu_item_id":43},
# {"index":1,"sku":"PAN-02","status":"unmapped","menu_item_id":44}]}
  • Resultado por producto:
    • mapped: quedó cruzado.
    • unmapped: se quitó el cruce.
    • not_found: no hay producto con ese SKU.
    • conflict: ese external_id ya lo tiene otro producto del negocio.
  • Regla del external_id: es único por negocio y no distingue mayúsculas.

Envía la existencia absoluta, no un delta, de hasta 500 productos por petición.

  • Cabecera obligatoria Idempotency-Key: de 8 a 100 caracteres (letras, números, . _ : -), única por lote. En un reintento se repite la misma: papedir no vuelve a aplicar lo que ya aplicó.
  • Cada ítem trae:
    • external_id o sku. Si vienen los dos, gana external_id.
    • stock: un número entre 0 y 99.999.999.999,999, con hasta 3 decimales.
    • Opcional, version: un entero que crece con cada cambio en tu sistema (un contador o updated_at en milisegundos).
    • Opcional, observed_at: una fecha ISO 8601; sirve cuando no hay versión.
  • Lo que llega tarde se descarta: si llega una versión o fecha menor o igual a la última aplicada, el ítem sale stale. Así no importa el orden en que lleguen los envíos.
Ventana de terminal
curl -s -X PUT "$BASE/stock" -H "Authorization: Bearer $LLAVE" \
-H 'Idempotency-Key: lote-2026-10-08T15:00:00Z' -H 'Content-Type: application/json' \
-d '{"items":[{"external_id":"b3f1c2d4-…","stock":12,"version":1728399600000},
{"sku":"PAN-02","stock":7.5}]}'
{"results":[
{"index":0,"external_id":"b3f1c2d4-…","sku":null,"menu_item_id":43,"status":"applied","stock_after":12},
{"index":1,"external_id":null,"sku":"PAN-02","menu_item_id":44,"status":"applied","stock_after":7.5}],
"summary":{"applied":2,"duplicate":0,"stale":0,"not_found":0,"untracked":0,"invalid":0}}
statusQué pasóQué hacer
appliedLa existencia quedó en stock—
duplicateEse lote ya se había aplicado (misma Idempotency-Key)Nada: es un reintento
staleLlegó una versión o fecha más vieja que la última aplicadaNada, o mandar la versión actual
not_foundNo hay producto vivo con ese external_id o skuHacer el cruce, o revisar si se borró
untrackedEl producto no lleva inventario en papedirPedirle al negocio que lo active
invalidÍtem mal formado, o el mismo producto repetido en el loteMandar un solo ítem por producto
  • Resultados en orden: la respuesta trae un resultado por ítem, en el orden en que se mandaron.
  • Un ítem malo no tumba el lote: los demás se aplican igual.
  • Rechazo del lote entero: solo pasa con 400, cuando el cuerpo no cumple el formato (VALIDATION_FAILED; error.details dice qué campo falla) o cuando falta la Idempotency-Key.

Cada pedido genera order.created, order.updated u order.cancelled. Recíbelos por webhook o léelos del feed GET /orders y confírmalos con POST /orders/ack (detalle en la referencia).

  • papedir sigue descontando cada pedido al crearlo. No deja vender lo que no hay entre dos envíos tuyos.
  • Tu sistema recibe el order.created, registra la venta y vuelve a mandar la existencia.
  • Reservas pendientes: mientras tu sistema no confirme un order.created (con 2xx del webhook o con ack), papedir resta esa venta a la existencia absoluta que mandes. El resultado de cada ítem trae reserved. Así un envío tuyo que todavía no incluye la venta no hace que papedir vuelva a vender lo que ya vendió.
    • La reserva dura como máximo 48 horas, o hasta que el evento queda dead (8 intentos fallidos). Después deja de restarse.
  • Confirma antes de mandar la existencia: responde 2xx (o haz ack) antes de enviar la existencia que ya incluye la venta. Si la mandas antes, papedir la resta dos veces hasta tu siguiente envío.
  • Cancelación: con order.cancelled, devuelve la existencia en tu sistema y mándala.
  • Primera vez:
    1. GET /catalog.
    2. Cruzar por SKU con PATCH /catalog/mapping.
    3. Un PUT /stock con todo.
  • Después: mandar solo lo que cambió, con version, y un envío completo al día como conciliación.
  • Reintentos: con la misma Idempotency-Key y espera exponencial ante 429 o 5xx. Un 4xx distinto de 429 no se reintenta sin corregir la petición.