Compras
Una compra (factura de proveedor o bill) registra la mercancía que ingresa de un proveedor. Al crearla, Zelta POS aumenta el stock de la bodega indicada y genera la cuenta por pagar correspondiente.
La URL base de producción es https://api-pos.zelta.dev/public/v1 y todas las rutas se expresan relativas a ella. Todas las peticiones deben hacerse sobre HTTPS e incluir tu API key. Consulta .
INFO
En los ítems de una compra, los productos se direccionan únicamente por referenceId (tu identificador externo). No se aceptan id interno, SKU ni código de barras.
Registrar una compra
POST /bills| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
providerId | string (cuid2) | No | Proveedor asociado a la compra. |
warehouseId | string (cuid2) | No | Bodega donde ingresa el stock. Por defecto, la bodega de la sucursal de la API key. |
date | string (ISO date) | No | Fecha de la compra. |
dueDate | string (ISO date) | No | Fecha de vencimiento del pago. |
note | string (≤1000) | No | Nota de la compra. |
items | array | Sí | Mínimo un ítem. |
items[].referenceId | string | Sí | Identificador externo de la variante. |
items[].quantity | number (>0) | Sí | Cantidad comprada. |
items[].price | number (≥0) | Sí | Costo de compra unitario. |
TIP
No se envía impuesto por línea: el ITBMS se calcula a partir de la tasa configurada en el catálogo de impuestos de cada producto.
curl -X POST "https://api-pos.zelta.dev/public/v1/bills" \
-H "Authorization: Bearer zpk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"providerId": "pr8h3k6m9p2q5v8r1n4t7w0y",
"date": "2026-01-15",
"dueDate": "2026-02-15",
"note": "Pedido de reposición mensual",
"items": [
{ "referenceId": "SKU-1001", "quantity": 24, "price": 8.50 },
{ "referenceId": "SKU-2050", "quantity": 10, "price": 15.00 }
]
}'Ejemplo JS
const res = await fetch('https://api-pos.zelta.dev/public/v1/bills', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.ZELTA_POS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
providerId: 'pr8h3k6m9p2q5v8r1n4t7w0y',
date: '2026-01-15',
dueDate: '2026-02-15',
note: 'Pedido de reposición mensual',
items: [
{ referenceId: 'SKU-1001', quantity: 24, price: 8.50 },
{ referenceId: 'SKU-2050', quantity: 10, price: 15.00 }
]
})
});
const data = await res.json();Ejemplo Py
import requests
res = requests.post(
'https://api-pos.zelta.dev/public/v1/bills',
headers={'Authorization': 'Bearer zpk_live_xxx', 'Content-Type': 'application/json'},
json={
'providerId': 'pr8h3k6m9p2q5v8r1n4t7w0y',
'date': '2026-01-15',
'dueDate': '2026-02-15',
'note': 'Pedido de reposición mensual',
'items': [
{ 'referenceId': 'SKU-1001', 'quantity': 24, 'price': 8.50 },
{ 'referenceId': 'SKU-2050', 'quantity': 10, 'price': 15.00 }
]
}
)
data = res.json()Respuesta 201 Created:
{
"id": "bl2k5m8p1q4v7r0n3t6w9y2z",
"number": "BILL-000231",
"providerId": "pr8h3k6m9p2q5v8r1n4t7w0y",
"warehouseId": "wh3b8n5k2j7h9g4f1d6s0a8q",
"status": "completed",
"paymentStatus": "unpaid",
"subtotal": 354.00,
"taxes": 24.78,
"total": 378.78,
"amountPaid": 0,
"balance": 378.78,
"note": "Pedido de reposición mensual",
"date": "2026-01-15",
"dueDate": "2026-02-15",
"items": [
{
"productVariantId": "pv9z2x4c6v8b1n3m5k7j9h2g",
"quantity": 24,
"unitPrice": 8.50,
"taxRate": 7,
"total": 218.28
},
{
"productVariantId": "pv1a3s5d7f9g2h4j6k8l0z2x",
"quantity": 10,
"unitPrice": 15.00,
"taxRate": 7,
"total": 160.50
}
],
"createdAt": "2026-01-15T14:10:00.000Z",
"updatedAt": "2026-01-15T14:10:00.000Z"
}El campo status puede ser draft, pending o completed. El paymentStatus puede ser unpaid, partial o paid. Los campos providerId, warehouseId, note, date y dueDate pueden ser null.
Problemas comunes
| Código | HTTP | Causa |
|---|---|---|
validation_error | 400 | Falta referenceId, quantity ≤ 0 o price negativo. |
unauthorized | 401 | API key ausente o inválida. |
not_found | 404 | El providerId, la warehouseId o un referenceId no existen. |
Listar compras
GET /billsAcepta los parámetros de : start, limit, updatedSince, from, to y metadata.
curl "https://api-pos.zelta.dev/public/v1/bills?from=2026-01-01&to=2026-01-31&metadata=true" \
-H "Authorization: Bearer zpk_live_xxx"Ejemplo JS
const res = await fetch('https://api-pos.zelta.dev/public/v1/bills?from=2026-01-01&to=2026-01-31&metadata=true', {
headers: { 'Authorization': `Bearer ${process.env.ZELTA_POS_API_KEY}` }
});
const data = await res.json();Ejemplo Py
import requests
res = requests.get(
'https://api-pos.zelta.dev/public/v1/bills',
headers={'Authorization': 'Bearer zpk_live_xxx'},
params={'from': '2026-01-01', 'to': '2026-01-31', 'metadata': 'true'}
)
data = res.json()Respuesta 200 OK:
{
"data": [
{
"id": "bl2k5m8p1q4v7r0n3t6w9y2z",
"number": "BILL-000231",
"providerId": "pr8h3k6m9p2q5v8r1n4t7w0y",
"warehouseId": "wh3b8n5k2j7h9g4f1d6s0a8q",
"status": "completed",
"paymentStatus": "unpaid",
"subtotal": 354.00,
"taxes": 24.78,
"total": 378.78,
"amountPaid": 0,
"balance": 378.78,
"date": "2026-01-15",
"dueDate": "2026-02-15"
}
],
"metadata": { "total": 231 }
}Obtener una compra
GET /bills/{id}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string (cuid2) | Sí | Identificador de la compra. |
Devuelve el objeto de la compra directamente (sin envoltorio), con la misma estructura que la respuesta de creación. Si no existe, responde 404 not_found.
Actualizar una compra
Solo se pueden editar los campos de cabecera. Las líneas de la compra no se modifican vía PUT. Si necesitas corregir ítems, anula la compra y vuelve a registrarla.
PUT /bills/{id}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
providerId | string (cuid2) o null | No | Cambia o elimina el proveedor. |
note | string o null | No | Actualiza la nota. |
date | string (ISO date) o null | No | Actualiza la fecha. |
dueDate | string (ISO date) o null | No | Actualiza el vencimiento. |
WARNING
Debes enviar al menos uno de los campos anteriores. Una petición con el cuerpo vacío devuelve validation_error.
curl -X PUT "https://api-pos.zelta.dev/public/v1/bills/bl2k5m8p1q4v7r0n3t6w9y2z" \
-H "Authorization: Bearer zpk_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "dueDate": "2026-03-01", "note": "Plazo extendido por acuerdo" }'Ejemplo JS
const res = await fetch('https://api-pos.zelta.dev/public/v1/bills/bl2k5m8p1q4v7r0n3t6w9y2z', {
method: 'PUT',
headers: { 'Authorization': `Bearer ${process.env.ZELTA_POS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ dueDate: '2026-03-01', note: 'Plazo extendido por acuerdo' })
});
const data = await res.json();Ejemplo Py
import requests
res = requests.put(
'https://api-pos.zelta.dev/public/v1/bills/bl2k5m8p1q4v7r0n3t6w9y2z',
headers={'Authorization': 'Bearer zpk_live_xxx', 'Content-Type': 'application/json'},
json={ 'dueDate': '2026-03-01', 'note': 'Plazo extendido por acuerdo' }
)
data = res.json()Respuesta 200 OK: devuelve el objeto de la compra actualizado. Si no existe, responde 404 not_found.
Problemas comunes
| Código | HTTP | Causa |
|---|---|---|
validation_error | 400 | Cuerpo vacío o tipos inválidos. |
unauthorized | 401 | API key ausente o inválida. |
not_found | 404 | La compra o el providerId no existen. |
Siguientes pasos
- — consulta cómo la compra impacta el kardex y el stock.
- — registra abonos sobre la cuenta por pagar.
- — registra egresos que no aumentan inventario.
- — descubre proveedores, bodegas e impuestos.