Skip to content

Referencia API ​

Las reglas comunes a todos los endpoints de la API de Zenci: URL base, formato de los datos, paginación, límites y errores.

URL base ​

https://api.zenci.app/public/v1

Todas las rutas de esta documentación son relativas a esa base y todas las peticiones van por HTTPS. Cada petición lleva una clave de API; consulta .

Endpoints ​

MétodoRutaPermiso de la clavePara qué sirve
GET/meNingunoVer qué permisos, sucursal y lista de precios tiene la clave
GET/productscatalog:readListar el catálogo, con sincronización incremental
GET/products/{id}catalog:readLeer un producto
GET/stockstock:readConsultar existencias por variante y bodega
GET/categoriescatalog:readListar las categorías
GET/brandscatalog:readListar las marcas
GET/branchesorders:write o stock:readListar las sucursales y la bodega de cada una
GET/warehousesorders:write o stock:readListar las bodegas
GET/payment-methodsorders:writeListar las formas de pago con las que se puede cobrar
POST/ordersorders:writeRegistrar una venta
GET/ordersorders:readListar las ventas que registró la clave
GET/orders/{id}orders:readLeer una venta con su factura
GET/invoices/{id}/pdfNinguno: enlace firmadoDescargar el PDF de una factura con el pdfUrl de la venta
GET/customerscustomers:readBuscar clientes
GET/customers/{id}customers:readLeer un cliente
POST/customerscustomers:writeCrear un cliente
PATCH/customers/{id}customers:writeCorregir un cliente
POST/customers/{id}/companiescustomers:writeAgregar una compañía a la que se le factura
DELETE/customers/{id}/companies/{companyId}customers:writeQuitar una compañía adicional

No hay más endpoints que estos. Los avisos (webhooks) no se configuran por la API: se crean en el panel. Consulta .

Una ruta que no existe responde 404 con el código not_found.

Especificación OpenAPI ​

La descripción completa de la API, en OpenAPI 3.1, está en:

https://api.zenci.app/public/v1/openapi.json

No pide clave. Trae todos los endpoints, sus parámetros, sus respuestas y los avisos (en la sección webhooks). Puedes importarla en Postman o Insomnia, o generar un cliente para tu lenguaje.

Formato de los datos ​

  • Cuerpo y respuestas en JSON. En POST /orders, envía el cuerpo en JSON con Content-Type: application/json.
  • Dinero como texto con dos decimales: "36.00", "2.52". No envíes ni esperes números de JSON en los importes.
  • Cantidades como texto, sin ceros de más: "1", "12", "0.5".
  • Tasa de impuesto como texto con dos decimales: "7.00", o null si el producto no lleva impuesto.
  • Fechas en ISO 8601, en UTC: "2026-10-06T15:56:12.000Z".
  • Los precios no incluyen el impuesto. El impuesto se suma al registrar la venta; consulta .

Recurso único ​

GET /products/{id}, GET /me, GET /orders/{id}, GET /customers/{id} y las altas y cambios (POST, PATCH) devuelven el objeto tal cual, sin envoltorio. DELETE responde 204 sin cuerpo.

Listas ​

GET /products, GET /stock, GET /orders y GET /customers envuelven los resultados en data y agregan pagination:

json
{
  "data": [ { "id": "k3v9x2m8q1w7e4r6t0y5u2i8" } ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 128,
    "totalPages": 3
  }
}

GET /payment-methods, GET /categories, GET /brands, GET /branches y GET /warehouses devuelven { "data": [ … ] }, sin pagination: las listas son cortas y salen completas.

Paginación ​

ParámetroTipoPor defectoDescripción
pageentero ≥ 11Página que quieres leer
limitentero de 1 a 10050Resultados por página

Un limit mayor que 100 se rechaza con 400; no se recorta en silencio. Para leer todo, avanza page hasta llegar a totalPages. GET /products además devuelve nextCursor, que es la forma segura de recorrerlo para sincronizar; consulta .

Parámetros desconocidos ​

La API rechaza cualquier parámetro o campo que no conozca, en la URL y en el cuerpo. Un ?limt=20 mal escrito o un campo discount en una línea de venta devuelven 400 validation_error, en vez de ignorarse y hacerte creer que se aplicaron.

json
{
  "code": "validation_error",
  "message": "Unrecognized key: \"limt\""
}

Límites de uso ​

LímiteValorQué pasa al superarlo
Peticiones por clave600 por minuto429 rate_limited
Peticiones sin clave, por IP200 por minuto429 rate_limited
Intentos con una clave inválida, por IP10 por minuto429 rate_limited y la IP queda bloqueada 60 segundos
Tamaño del cuerpo10 MB413 payload_too_large

El cupo de 600 es de cada clave, no de tu servidor: dos tiendas que comparten un mismo hosting no se gastan el cupo entre sí.

Cada respuesta autenticada trae el límite en estos encabezados:

EncabezadoValor
RateLimit-Policy600;w=60: 600 peticiones por ventana de 60 segundos
X-RateLimit-Limit600
Retry-AfterSolo en el 429: los segundos que conviene esperar antes de reintentar

La API no informa cuántas peticiones te quedan en la ventana: el límite lo aplica la red de Cloudflare y no lo expone. Regula tu ritmo con la política y, si recibes un 429, espera lo que dice Retry-After.

Errores ​

Todo error responde con un estado HTTP y un cuerpo plano con dos campos:

json
{
  "code": "not_found",
  "message": "Product not found"
}
HTTPcodeCuándo ocurre
400validation_errorParámetro o campo inválido, campo desconocido, cuerpo que no es JSON, falta X-Idempotency-Key, SKU que no existe, pago por encima del saldo con una forma de pago sin vuelto
401unauthorizedFalta la clave, o es desconocida, está revocada o venció
403forbiddenLa clave no tiene el permiso que pide la ruta, o la suscripción del comercio está restringida
404not_foundEl recurso no existe, la ruta no existe, o el comercio no tiene activa la API
409conflictLos pagos no cubren el total de la venta, el reenvío de una venta que todavía se registra (con Retry-After) o que no terminó bien, o un cliente que cambió desde que lo leíste (expectedUpdatedAt)
413payload_too_largeEl cuerpo pasa de 10 MB
429rate_limitedSuperaste un límite de uso
500internal_errorFalla inesperada del servidor

Decide qué hacer con el estado HTTP y el code. El message explica el motivo en inglés y sirve para tus registros, pero su texto puede cambiar: no lo compares en tu código.

Un 500 nunca trae detalle ("An unexpected error occurred"). Si te ocurre en POST /orders, reintenta con la misma X-Idempotency-Key; consulta .

Siguientes pasos ​

  • — Emite tu clave y envíala en cada petición.
  • — Lee el catálogo y sincronízalo.
  • — Registra una venta paso a paso.

Documentación oficial de Zelta