Ir al contenido
docs

Errores

El formato de los errores, el request_id y qué hacer con cada código.

Formato

Todo error responde con un código HTTP distinto de 2xx y este cuerpo:

JSON
{
  "error": {
    "code": "INSUFFICIENT_SCOPE",
    "message": "Esta API key no tiene el permiso \"sales:read\". Crea una key con ese permiso en cuenta.posdata.so → Desarrolladores.",
    "required_scope": "sales:read",
    "request_id": "req_6vJx0Qm2cPp1bK9a"
  }
}
CampoQué es
codeEl código estable para tu lógica. Ramifica por code, nunca por message. Los códigos no cambian de nombre ni se reutilizan.
messageExplicación en español para una persona, con el siguiente paso. Puede cambiar de redacción.
paramEl campo que causó el error, cuando aplica (por ejemplo sku, warehouse_id o Idempotency-Key). En errores de validación puede ser una ruta con puntos.
request_idEl identificador de la petición. También llega en el header Request-Id de todas las respuestas.

Algunos errores traen campos adicionales: required_scope (en INSUFFICIENT_SCOPE), feature (en PLAN_REQUIRED), retry_after (en RATE_LIMITED), devices (en DEVICES_OUTDATED) y available (en INSUFFICIENT_STOCK). Tu código debe tolerar campos nuevos.

Guarda el request_id en tus logs. Si escribes a soporte, inclúyelo: con él encontramos la petición exacta. Los errores nunca exponen detalles internos (trazas, SQL ni mensajes de otros servicios).

Códigos

Autenticación (401)

CódigoHTTPCuándo pasaQué hacer
UNAUTHENTICATED401Falta el header Authorization o no tiene la forma Bearer <key>.Envía Authorization: Bearer pdk_….
INVALID_API_KEY401La key no empieza por pdk_live_/pdk_test_ o no existe.Revisa que la copiaste completa. Si la perdiste, crea otra.
API_KEY_REVOKED401La key fue revocada.Crea una nueva en cuenta.posdata.so → Desarrolladores.
API_KEY_EXPIRED401La key llegó a su fecha de vencimiento.Crea una nueva y actualiza tu integración.
API_KEY_WRONG_ENVIRONMENT401Usas una key de pruebas en producción o al revés.pdk_live_ va con api.posdata.so; pdk_test_ con apidev.posdata.so.
ACCOUNT_INACTIVE401La cuenta dueña de la key está inactiva.Escribe a soporte.

Autorización (403)

CódigoHTTPCuándo pasaQué hacer
PLAN_REQUIRED403La cuenta no tiene el plan Pro (con o sin facturación electrónica) activo. Trae feature: "public_api".Las keys siguen guardadas y vuelven a funcionar cuando la cuenta tenga Pro. No reintentes en bucle.
INSUFFICIENT_SCOPE403La key no tiene el permiso del endpoint. Trae required_scope.Crea una key con ese permiso.
BROWSER_REQUESTS_NOT_ALLOWED403La petición trae el header Origin (viene de un navegador).Llama a la API desde tu servidor. Nunca pongas la key en un front-end.

La petición (400, 404, 413, 415)

CódigoHTTPCuándo pasaQué hacer
VALIDATION_ERROR400Un campo o parámetro no es válido, falta uno obligatorio, o enviaste uno que no existe (los filtros y campos desconocidos se rechazan, no se ignoran). También con 415 si el cuerpo no es JSON en UTF-8.Lee message y param, corrige y reintenta.
INVALID_JSON400El cuerpo no es JSON válido.Revisa la serialización y envía Content-Type: application/json.
PAYLOAD_TOO_LARGE413El cuerpo supera 256 KB.Envía menos datos por petición.
INVALID_CURSOR400starting_after no es un cursor válido.Usa el next_cursor de la respuesta anterior tal cual, sin modificarlo.
NOT_FOUND404El recurso no existe en esta cuenta (o el id no tiene formato de uuid).Verifica el id. Un id de otra cuenta responde igual que uno inventado.
ROUTE_NOT_FOUND404La ruta o el método no existen.Revisa la referencia.

Idempotencia

CódigoHTTPCuándo pasaQué hacer
IDEMPOTENCY_KEY_REQUIRED400Un POST sin header Idempotency-Key.Envía un valor único por operación (un UUID).
IDEMPOTENCY_KEY_REUSED422Usaste el mismo Idempotency-Key con una petición distinta.Usa un valor nuevo para cada operación distinta.
IDEMPOTENCY_IN_PROGRESS409La primera petición con ese Idempotency-Key todavía se está procesando, o se interrumpió antes de terminar.Espera unos segundos y reintenta con el mismo valor. Si sigue igual, verifica con un GET si el recurso se creó antes de usar un valor nuevo.

Detalle en Idempotencia.

Reglas de negocio (409, 422)

CódigoHTTPCuándo pasaQué hacer
CONFLICT409Ya existe un registro con esos datos únicos.Busca el registro existente en vez de crearlo otra vez.
SKU_ALREADY_EXISTS409Al editar una variante (PATCH /products/{id}/variants/{variantId}), otra variante de la cuenta ya usa ese sku. Trae param: "sku".Usa un SKU distinto o busca la variante que ya lo tiene.
NO_WAREHOUSE409Un movimiento o un conteo de stock (o un producto con initial_stock) sin warehouse_id, y la cuenta no tiene una bodega predeterminada: o todavía no hay bodegas sincronizadas desde las cajas, o hay varias y ninguna es la predeterminada.Indica warehouse_id (consúltalas con GET /warehouses) o abre Posdata en una caja con conexión para que sincronice sus bodegas.
DEVICES_OUTDATED409Al editar un producto o una variante (PATCH /products/{id} o PATCH /products/{id}/variants/{variantId}), algún dispositivo del negocio todavía no tiene la versión de Posdata que acepta ediciones sin stock. No se modificó nada. Trae devices: la lista de esos dispositivos, cada uno con name, kind, platform, app_version y last_seen_at.Abre esos dispositivos con internet para que se actualicen solos (tardan un minuto en reportarse) y reintenta, o revoca los que ya no uses en cuenta.posdata.so → Dispositivos. Crear productos y mover stock sí funciona mientras tanto. Mira por qué.
INSUFFICIENT_STOCK409Una salida (POST /inventory/movements con type: "out") pide más de lo que la API puede comprometer en esa bodega: el stock menos el margen de la API. No se registró nada. Trae param: "quantity" y available: lo que sí puedes sacar (puede ser 0).No reintentes la misma petición en bucle: saca como máximo available o marca el producto como agotado. El margen lo ajusta el negocio en cuenta.posdata.so → Inventario → Configuración. Mira Salidas atómicas.
STOCK_MODE_LEGACY409La cuenta todavía no lleva el stock en la nube: alguna de sus cajas principales aún no tiene la versión de Posdata que lo soporta. Lo responden GET /inventory/levels, GET /inventory/kardex, GET /inventory/valuation, POST /inventory/counts y los movimientos de variantes (POST /inventory/movements con variant_id o de un producto con variantes). No se registró nada.Pídele al negocio que abra sus cajas con internet para que se actualicen solas (y que revoque en cuenta.posdata.so → Dispositivos las que ya no use); el paso ocurre solo. Mientras tanto, los movimientos de productos sin variantes sí funcionan. Mira los dos modos.
COMPOSITE_STOCK_UNSUPPORTED422Intentaste mover o contar stock de un producto que se arma con receta.Mueve o cuenta el stock de sus insumos.
VARIANT_STOCK_UNSUPPORTED422Obsoleto: ya no se emite. Lo respondían los movimientos de productos con variantes antes de que las variantes movieran su propio stock. Se conserva en el catálogo hasta una nueva versión de la API.Nada. Si tu integración ramificaba por este código, trata STOCK_MODE_LEGACY de la misma forma.

Límites y servidor

CódigoHTTPCuándo pasaQué hacer
RATE_LIMITED429Superaste un límite de uso. Trae retry_after (segundos) y el header Retry-After.Espera lo que indica Retry-After y reintenta.
INTERNAL_ERROR500Falló algo de nuestro lado.Reintenta con espera exponencial. Si persiste, escríbenos con el request_id.

Qué reintentar

  • Sí: 429 (después de Retry-After), 409 IDEMPOTENCY_IN_PROGRESS, 500 y errores de red o tiempos de espera. Si es un POST, reintenta con el mismo Idempotency-Key.
  • No, sin cambiar algo antes: el resto de 4xx. Repetir la misma petición dará el mismo error. Por ejemplo, 409 INSUFFICIENT_STOCK solo cambia cuando entra stock y 409 STOCK_MODE_LEGACY, cuando la cuenta pasa al stock en la nube.