Zent para desarrolladores
Última actualización: 3 de noviembre de 2026
Si tu tienda está hecha a medida (no en Tiendanube ni en Shopify), la conectás vos: mandás tu catálogo, tus clientes, tus órdenes y tus carritos en el formato de Zent con el SDK @usezent/connect, y Zent arma las campañas de lifecycle como con cualquier otra tienda. Antes de conectar, Zent revisa lo que cargaste y te dice qué está mal, dónde y cómo arreglarlo.
Cómo se conecta
- Creá la tienda en Zent: Conectar tienda › Tienda propia (nombre, dirección, moneda y zona horaria). Te da la clave
zk_live_…, que se ve una sola vez. - Cargá la historia: productos, clientes, órdenes y carritos (el SDK lo hace en el orden correcto).
- Conectá: Zent corre los chequeos. Con un error, la tienda no se conecta y el reporte dice qué arreglar; sin errores, queda conectada y Zent arma sus campañas.
- Mandá cada cambio cuando pasa (tiempo real): una orden cobrada, un cliente nuevo, un cambio de stock, un carrito.
- Verificá tu dominio de envío en Zent › Dominios: los mails de una tienda propia salen sólo desde el dominio de la tienda (por ejemplo,
mail.mitienda.com.ar).
Instalar el SDK
Node 20.19 o más nuevo, sin dependencias. Valida cada registro antes de mandarlo con las mismas reglas que Zent.
npm install @usezent/connectimport { ZentConnect } from '@usezent/connect';
const zent = new ZentConnect({ apiKey: process.env.ZENT_API_KEY });
// 1. La historia: arrays o iterables async (se parten en lotes de 500).
const result = await zent.backfill({
products: allProducts(),
customers: allCustomers(),
orders: allOrders(),
checkouts: openCheckouts(),
});
// 2. Los chequeos de Zent: si algo está mal, la tienda no se conecta y te dice qué arreglar.
console.log(result.report.errors, result.report.warnings);La clave
Cada pedido lleva Authorization: Bearer zk_live_…. La clave identifica a la tienda: no hace falta mandar nada más. Guardala en el servidor (ZENT_API_KEY), nunca en el navegador ni en el repositorio. Si se filtra, generá otra en Zent: la anterior deja de valer en el acto.
Productos
El catálogo entero, también lo que ya no vendés (published: false): las órdenes viejas lo referencian. Cada combinación que se compra por separado es una variante.
| Campo | Tipo | Qué es |
|---|---|---|
| idobligatorio | id (texto o entero) | Id del producto en tu sistema (texto o número, hasta 64 caracteres). |
| nameobligatorio | texto (hasta 300) | Nombre visible del producto. |
| urlobligatorio | URL | URL pública de la página del producto, en el dominio de tu tienda. Los mails llevan acá. |
| handle | texto (hasta 200) · null | El slug de la URL, si lo tenés. |
| brand | texto (hasta 120) · null | Marca. |
| images | lista de URL https | URLs de las fotos en orden (https). La primera es la principal. |
| published | true / falsedefault true | false = no está a la venta; Zent no lo recomienda. |
| created_at | fecha ISO 8601 · null | Cuándo se dio de alta el producto. |
| categories | lista de objetos | Categorías del producto. Zent las usa para elegir qué recomendar. |
| categories[].idobligatorio | id (texto o entero) | Id de la categoría en tu sistema. |
| categories[].nameobligatorio | texto (hasta 200) | Nombre visible de la categoría. |
| categories[].parent_id | id (texto o entero) · null | Id de la categoría madre, si es una subcategoría. |
| variantsobligatorio | lista de objetos | Al menos una. Cada combinación que se compra por separado (talle, color…). |
| variants[].idobligatorio | id (texto o entero) | Id de la variante. Si tu tienda no maneja variantes, mandá una sola con el mismo id que el producto. |
| variants[].sku | texto (hasta 120) · null | SKU, si lo tenés. |
| variants[].priceobligatorio | monto (número ≥ 0) | Precio de venta actual, en la moneda de la tienda. |
| variants[].compare_at_price | monto (número ≥ 0) · null | Precio de lista antes de una rebaja (el tachado). Vacío si no está en oferta. |
| variants[].stockdeclarar | entero ≥ 0 · null | Unidades disponibles. null = ilimitado (no controlás stock). Con 0 no se recomienda en los mails. |
| variants[].visible | true / falsedefault true | false = la variante no se puede comprar en la tienda. |
| variants[].options | objeto {atributo: valor} | Atributos de la variante como objeto: {"Talle": "M", "Color": "Rojo"}. |
{
"id": "remera-basica",
"name": "Remera básica",
"url": "https://mitienda.com.ar/productos/remera-basica",
"images": [
"https://cdn.mitienda.com.ar/remera-basica-1.jpg"
],
"categories": [
{
"id": "remeras",
"name": "Remeras"
}
],
"variants": [
{
"id": "remera-basica-m",
"price": 15000,
"stock": 12,
"options": {
"Talle": "M"
}
},
{
"id": "remera-basica-l",
"price": 15000,
"stock": 0,
"options": {
"Talle": "L"
}
}
]
}Clientes
Quién compró o se registró. accepts_marketing es lo que eligió la persona: Zent no lo supone por vos.
| Campo | Tipo | Qué es |
|---|---|---|
| idobligatorio | id (texto o entero) | Id del cliente en tu sistema. |
| email · null | Mail del cliente. Sin mail se guarda, pero nunca recibe campañas. | |
| name | texto (hasta 200) · null | Nombre y apellido. |
| phone | texto (hasta 40) · null | Teléfono, como lo tengas. |
| created_atobligatorio | fecha ISO 8601 | Alta del cliente en tu tienda (no en Zent). Decide quién recibe la Bienvenida. |
| accepts_marketingdeclarar | true / false · null | true = aceptó recibir novedades; false = dijo que no; null = nunca se lo preguntaste. Obligatorio: Zent no lo supone. |
| accepts_marketing_at | fecha ISO 8601 · null | Cuándo dio (o sacó) el permiso, si lo guardás. |
{
"id": "1042",
"email": "ana@ejemplo.com",
"name": "Ana Pérez",
"created_at": "2026-10-01T14:30:00-03:00",
"accepts_marketing": true
}Órdenes
Todas las órdenes, con sus líneas. Sólo payment_status = paid cuenta como venta.
| Campo | Tipo | Qué es |
|---|---|---|
| idobligatorio | id (texto o entero) | Id de la orden en tu sistema. |
| number | entero ≥ 1 · null | Número de orden visible para el cliente, si es distinto del id. |
| customer | objeto | Quién compró: el id de Clientes y/o el mail. |
| customer.id | id (texto o entero) · null | Id del cliente (el `id` que mandás en Clientes). Vacío en una compra como invitado. |
| customer.email | email · null | Mail con el que compró. Con él se atribuyen las ventas a los mails. |
| created_atobligatorio | fecha ISO 8601 | Cuándo se hizo la compra. |
| currencyobligatorio | moneda ISO 4217 | Moneda (ISO 4217, ej. ARS). Tiene que ser la de tu tienda en Zent. |
| payment_statusobligatorio | uno de los valores | Estado del pago.
|
| paid_at | fecha ISO 8601 · null | Cuándo se cobró. Obligatorio si payment_status es paid. |
| status | uno de los valoresdefault "open" | Estado de la orden.
|
| cancelled_at | fecha ISO 8601 · null | Cuándo se canceló. |
| subtotalobligatorio | monto (número ≥ 0) | Suma de las líneas (precio × cantidad), antes de descuentos y envío. |
| discounts | objeto | Descuentos de la orden, por tipo. |
| discounts.coupon | monto (número ≥ 0)default 0 | Descuento por cupón. |
| discounts.promotion | monto (número ≥ 0)default 0 | Descuento por promociones automáticas (2x1, % por categoría…). |
| discounts.gateway | monto (número ≥ 0)default 0 | Descuento por medio de pago (transferencia, efectivo…). |
| shipping | monto (número ≥ 0)default 0 | Lo que pagó el cliente por el envío. |
| totalobligatorio | monto (número ≥ 0) | Lo que pagó en total. |
| coupon_codes | lista de texto (hasta 60) | Códigos de cupón usados. Con ellos Zent atribuye las ventas de los mails con cupón. |
| itemsobligatorio | lista de objetos | Las líneas de la orden. |
| items[].product_idobligatorio | id (texto o entero) | Id del producto (el mismo `id` que mandás en Productos). |
| items[].variant_id | id (texto o entero) · null | Id de la variante (el mismo `id` de `variants`). Vacío si el producto no tiene variantes. |
| items[].nameobligatorio | texto (hasta 300) | Nombre del producto como figura en la orden. |
| items[].sku | texto (hasta 120) · null | SKU de la línea. |
| items[].quantityobligatorio | entero ≥ 1 | Unidades compradas. |
| items[].unit_priceobligatorio | monto (número ≥ 0) | Precio por unidad que pagó, antes de descuentos de la orden. |
| items[].image_url | URL https · null | Foto del producto (https). |
| items[].zent_offer | id (texto o entero) · null | Si la línea la sumó la oferta del cross-sell de Zent en tu checkout: el id de la oferta (offer.id). Con eso esa venta se le atribuye a Zent. |
| shipped_at | fecha ISO 8601 · null | Cuándo se despachó. |
| tracking | objeto · null | Seguimiento del envío. |
| tracking.number | texto (hasta 120) · null | Número de seguimiento del envío. |
| tracking.url | URL · null | Link para seguir el envío (va en el mail de Post-compra). |
{
"id": "A-20931",
"customer": {
"id": "1042",
"email": "ana@ejemplo.com"
},
"created_at": "2026-10-28T19:02:00-03:00",
"paid_at": "2026-10-28T19:03:10-03:00",
"currency": "ARS",
"payment_status": "paid",
"status": "open",
"subtotal": 30000,
"discounts": {
"coupon": 3000
},
"shipping": 2500,
"total": 29500,
"coupon_codes": [
"HOLA10"
],
"items": [
{
"product_id": "remera-basica",
"variant_id": "remera-basica-m",
"name": "Remera básica · M",
"quantity": 2,
"unit_price": 15000
}
]
}Carritos
Carritos con mail que no terminaron en compra. Cuando un carrito se convierte, alcanza con mandar la orden: Zent lo cierra solo (por el mail o el cliente).
| Campo | Tipo | Qué es |
|---|---|---|
| idobligatorio | id (texto o entero) | Id del carrito en tu sistema. |
| emailobligatorio | Mail que dejó en el checkout. Sin mail no hay a quién escribirle. | |
| name | texto (hasta 200) · null | Nombre, si lo dejó. |
| phone | texto (hasta 40) · null | Teléfono, si lo dejó. |
| customer_id | id (texto o entero) · null | Id del cliente, si estaba logueado. |
| created_atobligatorio | fecha ISO 8601 | Cuándo empezó el checkout. |
| updated_atobligatorio | fecha ISO 8601 | Último cambio del carrito. |
| recovery_urlobligatorio | URL | Link que vuelve a abrir ESE carrito con sus productos, en el dominio de tu tienda. Es el botón del mail. |
| currencyobligatorio | moneda ISO 4217 | Moneda (ISO 4217). La de tu tienda. |
| totalobligatorio | monto (número ≥ 0) | Total del carrito. |
| accepts_marketing | true / false · null | Si marcó «quiero recibir novedades» en el checkout. null = no se lo preguntaste. |
| itemsobligatorio | lista de objetos | Los productos del carrito. |
| items[].product_idobligatorio | id (texto o entero) | Id del producto (el mismo `id` que mandás en Productos). |
| items[].variant_id | id (texto o entero) · null | Id de la variante (el mismo `id` de `variants`). Vacío si el producto no tiene variantes. |
| items[].nameobligatorio | texto (hasta 300) | Nombre del producto como figura en la orden. |
| items[].sku | texto (hasta 120) · null | SKU de la línea. |
| items[].quantityobligatorio | entero ≥ 1 | Unidades compradas. |
| items[].unit_priceobligatorio | monto (número ≥ 0) | Precio por unidad que pagó, antes de descuentos de la orden. |
| items[].image_url | URL https · null | Foto del producto (https). |
| items[].zent_offer | id (texto o entero) · null | El id de la oferta del cross-sell de Zent, si la sumó ella. Opcional: la venta se atribuye por la orden. |
{
"id": "cart_88f1",
"email": "ana@ejemplo.com",
"created_at": "2026-11-01T21:10:00-03:00",
"updated_at": "2026-11-01T21:14:00-03:00",
"recovery_url": "https://mitienda.com.ar/carrito/recuperar/cart_88f1",
"currency": "ARS",
"total": 15000,
"accepts_marketing": true,
"items": [
{
"product_id": "remera-basica",
"variant_id": "remera-basica-l",
"name": "Remera básica · L",
"quantity": 1,
"unit_price": 15000
}
]
}Ids
- Un id es un texto o un entero de hasta 64 caracteres: el de tu base (un número, un UUID, un SKU). Se guarda tal cual.
- Usá los mismos ids en todos lados:
items[].product_idyvariant_idson losidque mandás en Productos. - Si un producto no tiene variantes, mandá una con el mismo id que el producto y usá ese id como variant_id.
- Mandar un registro que ya está lo actualiza (upsert por id): podés reenviar sin duplicar.
Tiempo real
Con la tienda conectada, cada cambio que mandás entra al momento. Una orden cobrada en las últimas 48 horas recalcula al comprador y lo que le toca; un cliente registrado en la última hora puede recibir la Bienvenida. Lo más viejo entra como historia y lo toma la vuelta diaria. Que un error de Zent nunca te rompa la compra: atrapalo y reintentá.
// Después de crear o cobrar una orden:
await zent.orders.upsert([toZentOrder(order)]);
// Cuando se registra un cliente:
await zent.customers.upsert([toZentCustomer(customer)]);
// Cuando cambia el precio o el stock:
await zent.products.upsert([toZentProduct(product)]);
// Cuando un carrito queda con mail y sin comprar (por ejemplo, a la hora):
await zent.checkouts.upsert([toZentCheckout(cart)]);Cupones
Los cupones de tu tienda los guarda Zent (los de cada mail, el del pop-up, el del carrito, el beneficio por monto). Tu checkout los valida y los canjea con el SDK, y Zent los empieza a usar recién cuando lo activás: hasta entonces los mails salen sin cupón.
- Validar cuando el comprador escribe un código:
zent.coupons.validate(code, { subtotal }). Un cupón que no vale no tira error: vuelvevalid: falsecon el motivo y un texto para mostrarle. - Canjear al crear la orden:
zent.coupons.redeem(code, { orderId, subtotal }). Con el mismo orderId no gasta otro uso: podés reintentar. - Mandar el código en la orden (
coupon_codes), tal como lo emitió Zent: así esa venta se le atribuye. Una orden cobrada con un código de Zent cuenta como canje aunque no hayas llamado a redeem. - El link del carrito abandonado trae el cupón en
?zent_coupon=CÓDIGO: aplicalo solo al abrir el carrito. - Activar, una vez, cuando todo lo anterior ya anda:
zent.coupons.enable().
// Cuando el comprador escribe un código:
const v = await zent.coupons.validate(code, { subtotal: cart.subtotal });
if (!v.valid) return showError(v.message); // «Ese cupón venció.», «Ese cupón ya se usó.»…
applyDiscount(v.discount); // envío gratis: v.coupon.type === 'free_shipping'
// Al crear la orden (idempotente por orderId):
await zent.coupons.redeem(code, { orderId: order.id, subtotal: cart.subtotal });
// Una vez, cuando todo esto ya anda en producción:
await zent.coupons.enable();| Código | Qué pasó y cómo se arregla |
|---|---|
| not_found | Ese cupón no existe. No es un cupón de Zent: si tu tienda tiene cupones propios, probalo contra los tuyos. |
| disabled | Ese cupón ya no está activo. Zent lo dio de baja (por ejemplo, se apagó el beneficio). |
| not_started | Ese cupón todavía no está vigente. Todavía no llegó su fecha de inicio. |
| expired | Ese cupón venció. Pasó su fecha de fin (los días son de la hora de Argentina). |
| used_up | Ese cupón ya se usó. Ya se usó todas las veces que permite. |
| min_purchase | Este cupón pide una compra mínima. El subtotal no llega al mínimo: viene en min_purchase para mostrárselo. |
Pop-up y vistas de producto
El pop-up de captación suma suscriptos con su cupón de bienvenida, y el mismo script registra qué productos mira cada contacto que llega desde un mail de Zent: con eso sale el Abandono de navegación. Se activa en Zent › Add-ons (pide los cupones activos) y se instala pegando un script en todas las páginas de tu tienda:
<script src="https://usezent.xyz/zent-capture.js" data-zent-store="<id de tu tienda en Zent>" defer></script>En la página de un producto, decile cuál es con una meta (el mismo id que mandás en Productos). Si tu sitio cambia de producto sin recargar la página, llamá a Zent.viewProduct(id) en cada cambio.
<meta name="zent:product" content="remera-basica">
// Si tu sitio cambia de producto sin recargar:
Zent.viewProduct('remera-basica');Cross-sell en el checkout
Zent elige el producto que mejor acompaña el carrito (el mismo motor que en Tiendanube) y tu checkout lo muestra. Corre en el navegador, sin clave: @usezent/connect/storefront habla con los ids de tu tienda. Se activa en Zent › Add-ons.
import { ZentStorefront } from '@usezent/connect/storefront';
const zent = new ZentStorefront({ store: '<id de tu tienda en Zent>' });
const { offer } = await zent.offer({
cartId: cart.id,
subtotal: cart.subtotal,
items: cart.items.map((i) => ({ product_id: i.productId })), // tus ids
});
if (offer) {
render(offer.items); // productId y variants[].id ya son los de tu tienda
zent.offerEvent({ offerId: offer.id, type: 'shown' });
}
// Al sumarlo:
await addToCart({ productId, variantId, quantity: 1, zent_offer: offer.id });
zent.offerEvent({ offerId: offer.id, type: 'added', productId, variantId, quantity: 1 });- La línea que suma el producto lleva
zent_offer: offer.id, y la orden la manda igual (items[].zent_offer): así esa venta se le atribuye a Zent. - Los eventos (
shown,added,dismissed,completed) sólo ajustan cómo aprende: la venta sale de la orden cobrada. - Si activás el beneficio por monto, la respuesta trae
benefit; al llegar al objetivo,claimBenefitda un cupón de un solo uso para ese carrito.
Conectar y chequeos
zent.connect() (o npx @usezent/connect check, que sale con código 1 si hay errores) mira lo cargado en conjunto, lo que un registro solo no muestra. Las reglas entre órdenes se miden sobre las últimas 5.000. Un error frena la conexión; un aviso conecta y te dice qué no va a funcionar. Ya conectada, revisar de nuevo nunca la desconecta.
ZENT_API_KEY=zk_live_… npx @usezent/connect check
✗ No pasó los chequeos: la tienda no se conectó.
Miramos: products 412 · customers 9310 · orders 22071 · checkouts 0 (las reglas entre órdenes, sobre las últimas 5000)
Errores (arreglalos para conectar):
[items_unknown_products] Órdenes con productos que no llegaron: 1204 de 9877 líneas (12,2 %) apuntan a un producto o una variante que no llegó (1204 por el producto, 0 por la variante).
→ Hay líneas cuyo product_id (o variant_id) no está en lo que mandaste en Productos. Usá los mismos ids en los dos lados y cargá también los productos que ya no vendés.
https://usezent.xyz/desarrolladores#chequeos-items_unknown_products| Código | Qué pasó y cómo se arregla |
|---|---|
| no_productsfrena la conexión | No llegó ningún producto. Cargá el catálogo (products) antes de conectar: sin productos no hay nada que recomendar. |
| items_unknown_productsfrena la conexión | Órdenes con productos que no llegaron. Hay líneas cuyo product_id (o variant_id) no está en lo que mandaste en Productos. Usá los mismos ids en los dos lados y cargá también los productos que ya no vendés. |
| no_paid_ordersfrena la conexión | Hay órdenes pero ninguna está cobrada. Revisá cómo traducís el estado del pago: una venta cobrada es payment_status = paid. |
| subtotal_mismatchfrena la conexión | El subtotal no coincide con las líneas. En muchas órdenes subtotal ≠ Σ (unit_price × quantity). subtotal es la suma de las líneas antes de descuentos y envío. |
| consent_all_truefrena la conexión | Todos los clientes aceptan marketing. Ningún cliente tiene accepts_marketing = false: casi siempre es un default. Mandá el permiso real; si no lo preguntás, null. |
| no_customersaviso | Sin clientes. Zent arranca en frío: no hay a quién escribirle hasta que lleguen clientes nuevos. |
| no_ordersaviso | Sin órdenes. Sin historia de compras, Zent no puede armar las etapas de tus clientes. Cargá las órdenes viejas si las tenés. |
| orders_unknown_customersaviso | Órdenes de clientes que no llegaron. Hay órdenes con customer.id que no está en Clientes. Cargá todos los clientes o mandá sólo el mail. |
| total_mismatchaviso | El total no cierra. En muchas órdenes total ≠ subtotal − descuentos + envío. Si sumás impuestos aparte está bien; si no, revisá los descuentos. |
| orders_without_emailaviso | Órdenes sin mail. Sin el mail del comprador esas ventas no se pueden atribuir. Mandá customer.email. |
| no_checkoutsaviso | Sin carritos abandonados. La campaña de Carrito abandonado no va a salir hasta que mandes checkouts. |
| coupons_not_enabledaviso | Los cupones de Zent no están activos. Cuando tu checkout ya valide los cupones de Zent (zent.coupons.validate / redeem), llamá una vez zent.coupons.enable(). Hasta entonces los mails y el pop-up salen sin cupón. |
| domain_not_sendingaviso | Tu dominio de envío todavía no está verificado. La tienda conecta, pero los mails de una tienda propia salen sólo desde su dominio: verificalo en Zent › Dominios. |
Errores
Un lote es todo o nada: con un registro mal no se escribe ninguno, y la respuesta trae todos los errores. Cada uno con un code estable, dónde está (path), el id del registro, cómo arreglarlo y el link a esta página. Nunca repite un mail, un nombre ni un teléfono.
HTTP/1.1 422
{
"ok": false,
"errors": [
{
"code": "invalid_enum",
"path": "data[3].payment_status",
"record_id": "A-20934",
"message": "«payment_status» tiene que ser uno de: paid, pending, authorized, partially_refunded, refunded, voided (recibimos completed).",
"hint": "Traducí tu valor al vocabulario de Zent (la tabla de valores está en la documentación).",
"docs": "https://usezent.xyz/desarrolladores#ordenes-payment_status"
}
]
}import { ZentValidationError } from '@usezent/connect';
try {
await zent.orders.upsert(orders);
} catch (e) {
if (e instanceof ZentValidationError) console.error(e.toString()); // o e.errors
else throw e;
}| Código | Qué pasó y cómo se arregla |
|---|---|
| required | Falta un campo obligatorio. Mandá el campo con un valor. Revisá en la tabla del recurso cuáles son obligatorios. |
| required_key | Falta un campo que tenés que declarar. Este campo puede ser null, pero tiene que venir: es un dato que Zent no puede suponer por vos. |
| unknown_field | Campo desconocido. Los campos van en snake_case y con los nombres de la documentación. Un campo de más casi siempre es un typo. |
| invalid_type | Tipo incorrecto. Revisá el tipo del campo en la tabla del recurso. |
| invalid_enum | Valor no permitido. Traducí tu valor al vocabulario de Zent (la tabla de valores está en la documentación). |
| invalid_id | Id inválido. Un id es un texto o un número entero, sin espacios en los bordes y de hasta 64 caracteres. |
| invalid_email | Mail inválido. Mandá el mail completo (nombre@dominio.com) o null si no lo tenés. |
| invalid_url | URL inválida. Mandá una URL absoluta que empiece con https:// (o http://). |
| url_not_https | La URL tiene que ser https. Las fotos de los mails sólo se ven con https. |
| url_outside_store | URL fuera del dominio de tu tienda. Los mails sólo llevan a tu tienda: la URL tiene que ser del dominio que cargaste al crear la tienda en Zent (o de un subdominio). |
| invalid_datetime | Fecha inválida. Mandá la fecha en ISO 8601 con zona horaria, por ejemplo 2026-11-02T14:30:00-03:00 o 2026-11-02T17:30:00Z. |
| datetime_without_timezone | Fecha sin zona horaria. Sin zona horaria la hora es ambigua: agregá el offset (-03:00) o la Z de UTC. |
| datetime_in_future | Fecha en el futuro. Casi siempre es la zona horaria mal convertida o milisegundos donde van segundos. |
| not_integer | Tiene que ser un número entero. Sin decimales. |
| out_of_range | Número fuera de rango. Revisá el mínimo del campo (los montos y las cantidades no pueden ser negativos). |
| too_long | Texto demasiado largo. Recortalo al máximo del campo. |
| too_few_items | Faltan elementos en la lista. La lista tiene un mínimo de elementos. |
| too_many_items | Demasiados elementos en la lista. La lista tiene un máximo de elementos. |
| duplicate_id | Id repetido. Dentro de un mismo envío (o de una lista) cada id va una sola vez. |
| invalid_currency | Moneda inválida. Mandá el código ISO 4217 de tres letras en mayúsculas (ARS, USD, MXN…). |
| currency_mismatch | Otra moneda que la de tu tienda. Zent mide en la moneda de tu tienda. Si vendés en varias, mandá los montos convertidos a ésa. |
| paid_without_paid_at | Orden cobrada sin fecha de cobro. Con payment_status = paid, mandá paid_at: el mail de Post-compra se programa desde ahí. |
| paid_before_created | Cobro muy anterior a la compra. paid_at queda más de un día antes que created_at: casi seguro que están cruzados. |
| id_collision | Dos ids distintos chocan. Dos ids de productos o variantes distintos quedaron con el mismo número interno (pasa una vez en millones). Avisanos desde el panel de Zent (el botón de comentarios) con los dos ids. |
| invalid_json | El cuerpo no es JSON. Mandá Content-Type: application/json y un cuerpo JSON válido. |
| invalid_body | Cuerpo con otra forma. El cuerpo es { "data": [ …registros… ] } (y, si querés, "validate_only": true). |
| empty_batch | Envío vacío. `data` tiene que traer al menos un registro. |
| batch_too_large | Demasiados registros. Mandá hasta 500 registros por pedido (el SDK los parte solo). |
| payload_too_large | Cuerpo demasiado grande. Mandá menos registros por pedido. |
| unauthorized | Clave inválida. Mandá el header Authorization: Bearer zk_live_… con la clave de tu tienda (en Zent: el panel de la conexión). Una clave regenerada deja de valer la anterior. |
| rate_limited | Demasiados pedidos. Esperá los segundos de Retry-After y reintentá (el SDK lo hace solo). |
| not_found | Ruta inexistente. Revisá la URL: las rutas son /api/connect/v1/<recurso>. |
| server_error | Error de Zent. No es tu integración: reintentá en unos segundos. Si sigue, avisanos desde el panel de Zent (el botón de comentarios). |
Límites
- Hasta 500 registros y 4 MB por pedido (el SDK parte solo).
- Hasta 240 pedidos por minuto por tienda: una carga de 100.000 órdenes son 200 pedidos. Pasado eso, 429 con Retry-After (el SDK espera y reintenta).
- Las fechas van en ISO 8601 con zona horaria y nunca más de un día en el futuro.
- Montos en la moneda de tu tienda en Zent, ≥ 0.
Borrar un cliente
Si un cliente te pide borrar sus datos, pedíselo también a Zent: sus órdenes quedan sin datos personales, el cliente se borra y su mail no vuelve a recibir nada.
await zent.customers.redact({ ids: ['1042'] });
// o por mail: await zent.customers.redact({ emails: ['ana@ejemplo.com'] });API HTTP
El SDK es una capa fina: lo mismo se hace con cualquier cliente HTTP.
POST /api/connect/v1/products·customers·orders·checkouts: cuerpo{ "data": [...] }; con"validate_only": truevalida sin escribir.POST /api/connect/v1/connect: los chequeos (y conectar).GET /api/connect/v1/status: la tienda, cuánto llegó, el último reporte y el dominio de envío.POST /api/connect/v1/redact:{ "customer_ids": [...], "emails": [...] }.POST /api/connect/v1/coupons/validate·redeem·enable:{ "code", "subtotal", "order_id" }(Cupones).POST /api/connect/v1/storefront/offer·event: desde el navegador, sin clave, con"store"(Cross-sell).
curl https://usezent.xyz/api/connect/v1/orders \
-H "Authorization: Bearer $ZENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": [ { "id": "A-20931", "...": "..." } ] }'Con un agente de IA
Si la integración la escribe un agente (Claude Code, Cursor, Codex), el paquete trae AGENTS.md con las reglas: leer el código y no los datos de tus clientes, el permiso de marketing explícito, cómo traducir los estados y dónde enganchar el tiempo real. Pegale esto:
Conectá esta tienda con Zent usando @usezent/connect. Seguí al pie de la letra
node_modules/@usezent/connect/AGENTS.md y https://usezent.xyz/desarrolladores:
mapeos puros por recurso, un script de carga inicial y los enganches de tiempo real.
No leas datos reales de clientes. Terminá cuando `npx @usezent/connect check` dé ✓.