Productos
Gestiona el catálogo de productos y servicios de tu workspace. Los productos pueden ser físicos, servicios, suscripciones o digitales, y se utilizan en cotizaciones, órdenes y otros módulos de comercio.
Autenticación
Requiere header Authorization: Bearer {token}. Ver .
Listar Productos
GET /productsRetorna una lista paginada del catálogo de productos del workspace.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
cursor | string | No | Cursor de paginación retornado en la respuesta anterior |
limit | integer | No | Resultados por página. Mín: 1, máx: 100. Default: 25 |
sort | string | No | Campo de ordenamiento: created_at, name, base_price. Default: created_at |
order | string | No | Dirección: asc o desc. Default: desc |
search | string | No | Búsqueda por texto en nombre, SKU y descripción |
type | string | No | Tipo de producto: physical, service, subscription, digital |
status | string | No | Estado: active, archived, draft |
category | string | No | Filtra por categoría exacta |
owner_id | string | No | Filtra por ID del responsable del producto |
Permisos
Requiere permiso products:read. El módulo de Productos debe estar activo en el workspace.
Ejemplo
curl -X GET "https://api-crm.zelta.dev/public/v1/products?type=service&status=active&limit=25" \
-H "Authorization: Bearer tu-api-token-aqui"Ejemplo JS
const params = new URLSearchParams({
type: 'service',
status: 'active',
limit: '25'
});
const response = await fetch(
`https://api-crm.zelta.dev/public/v1/products?${params}`,
{
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_TOKEN}`
}
}
);
const result = await response.json();
console.log(result.data); // array de productosEjemplo Py
import requests
response = requests.get(
'https://api-crm.zelta.dev/public/v1/products',
params={
'type': 'service',
'status': 'active',
'limit': 25
},
headers={
'Authorization': 'Bearer tu-api-token-aqui'
}
)
result = response.json()
print(result['data'])Respuesta exitosa
HTTP 200 OK
{
"data": [
{
"id": "pK4nBvCxDyEz",
"name": "Consultoría de implementación",
"type": "service",
"category": "Consultoría",
"sku": "SRV-IMPL-001",
"description": "Servicio de implementación y configuración del CRM",
"basePrice": "1500.00",
"currency": "USD",
"taxRate": "7.00",
"unit": "hora",
"status": "active",
"ownerId": "aB3kLmNpQrSt",
"ownerName": "Carlos Herrera",
"hasVariants": false,
"metadata": {},
"sortOrder": 1,
"createdAt": "2026-01-15T09:00:00.000Z",
"updatedAt": "2026-03-10T11:30:00.000Z"
}
],
"pagination": {
"nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTAxLTE1VDA5OjAwOjAwLjAwMFoiLCJpZCI6InBLNG5CdkN4RHlFeiJ9",
"hasMore": false
}
}Obtener Producto
GET /products/:idRetorna el detalle completo de un producto, incluyendo sus planes o variantes.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string | Sí | ID del producto |
Ejemplo
curl -X GET "https://api-crm.zelta.dev/public/v1/products/pK4nBvCxDyEz" \
-H "Authorization: Bearer tu-api-token-aqui"Ejemplo JS
const id = 'pK4nBvCxDyEz';
const response = await fetch(
`https://api-crm.zelta.dev/public/v1/products/${id}`,
{
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_TOKEN}`
}
}
);
const result = await response.json();
console.log(result.data);Ejemplo Py
import requests
product_id = 'pK4nBvCxDyEz'
response = requests.get(
f'https://api-crm.zelta.dev/public/v1/products/{product_id}',
headers={
'Authorization': 'Bearer tu-api-token-aqui'
}
)
result = response.json()
print(result['data'])Respuesta exitosa
HTTP 200 OK
{
"data": {
"id": "pK4nBvCxDyEz",
"name": "Consultoría de implementación",
"type": "service",
"category": "Consultoría",
"sku": "SRV-IMPL-001",
"description": "Servicio de implementación y configuración del CRM",
"basePrice": "1500.00",
"currency": "USD",
"taxRate": "7.00",
"unit": "hora",
"status": "active",
"ownerId": "aB3kLmNpQrSt",
"ownerName": "Carlos Herrera",
"hasVariants": false,
"metadata": {},
"sortOrder": 1,
"plans": [],
"createdAt": "2026-01-15T09:00:00.000Z",
"updatedAt": "2026-03-10T11:30:00.000Z"
}
}Crear Producto
POST /productsCrea un nuevo producto en el catálogo del workspace.
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del producto. Máx: 200 caracteres |
type | string | Sí | Tipo de producto: physical, service, subscription, digital |
category | string | No | Categoría del producto. Máx: 100 caracteres |
sku | string | No | Código de referencia interno (SKU). Máx: 50 caracteres |
description | string | No | Descripción completa del producto |
basePrice | string | No | Precio base en formato decimal. Ej: "99.99" |
currency | string | No | Código de moneda ISO 4217. Ej: "USD", "MXN". Default: moneda del workspace |
taxRate | string | No | Tasa de impuesto en formato decimal. Ej: "7.00" para 7% |
unit | string | No | Unidad de medida o venta. Máx: 20 caracteres. Ej: "hora", "unidad", "mes" |
status | string | No | Estado inicial: active, archived, draft. Default: active |
ownerId | string | No | ID del usuario responsable. Default: el usuario de la API key |
hasVariants | boolean | No | Indica si el producto físico tiene variantes/planes. Default: false. Solo aplica para tipo physical |
metadata | object | No | Datos adicionales en formato clave-valor libre |
sortOrder | integer | No | Orden de aparición en listados. Default: 0 |
Tipos de producto y variantes
physical: Productos físicos con inventario. Soporta variantes cuandohasVariants: true.service: Servicios por hora o proyecto. Siempre muestra la sección de planes.subscription: Servicios recurrentes con ciclo de facturación. Siempre muestra planes.digital: Descargas, licencias o accesos digitales. Siempre muestra planes.
Ejemplo
curl -X POST "https://api-crm.zelta.dev/public/v1/products" \
-H "Authorization: Bearer tu-api-token-aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Plan CRM Profesional",
"type": "subscription",
"category": "Software",
"sku": "CRM-PRO-MES",
"description": "Acceso completo al CRM con hasta 10 usuarios y soporte prioritario.",
"basePrice": "299.00",
"currency": "USD",
"taxRate": "7.00",
"unit": "mes",
"status": "active"
}'Ejemplo JS
const response = await fetch('https://api-crm.zelta.dev/public/v1/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Plan CRM Profesional',
type: 'subscription',
category: 'Software',
sku: 'CRM-PRO-MES',
description: 'Acceso completo al CRM con hasta 10 usuarios y soporte prioritario.',
basePrice: '299.00',
currency: 'USD',
taxRate: '7.00',
unit: 'mes',
status: 'active'
})
});
const result = await response.json();
console.log(result.data.id);Ejemplo Py
import requests
response = requests.post(
'https://api-crm.zelta.dev/public/v1/products',
headers={
'Authorization': 'Bearer tu-api-token-aqui',
'Content-Type': 'application/json'
},
json={
'name': 'Plan CRM Profesional',
'type': 'subscription',
'category': 'Software',
'sku': 'CRM-PRO-MES',
'description': 'Acceso completo al CRM con hasta 10 usuarios y soporte prioritario.',
'basePrice': '299.00',
'currency': 'USD',
'taxRate': '7.00',
'unit': 'mes',
'status': 'active'
}
)
result = response.json()
print(result['data']['id'])Respuesta exitosa
HTTP 201 Created
{
"data": {
"id": "qL5oCwDyEzFa",
"name": "Plan CRM Profesional",
"type": "subscription",
"category": "Software",
"sku": "CRM-PRO-MES",
"description": "Acceso completo al CRM con hasta 10 usuarios y soporte prioritario.",
"basePrice": "299.00",
"currency": "USD",
"taxRate": "7.00",
"unit": "mes",
"status": "active",
"ownerId": "aB3kLmNpQrSt",
"ownerName": "Carlos Herrera",
"hasVariants": false,
"metadata": {},
"sortOrder": 0,
"createdAt": "2026-04-09T10:00:00.000Z",
"updatedAt": "2026-04-09T10:00:00.000Z"
}
}Actualizar Producto
PUT /products/:idActualiza los campos de un producto existente. Todos los campos son opcionales — solo se modifican los campos enviados.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string | Sí | ID del producto |
Cuerpo de la solicitud
Los mismos campos que en , todos opcionales.
Ejemplo
curl -X PUT "https://api-crm.zelta.dev/public/v1/products/qL5oCwDyEzFa" \
-H "Authorization: Bearer tu-api-token-aqui" \
-H "Content-Type: application/json" \
-d '{
"basePrice": "349.00",
"description": "Acceso completo al CRM con hasta 10 usuarios, soporte prioritario y módulo de automatizaciones."
}'Ejemplo JS
const id = 'qL5oCwDyEzFa';
const response = await fetch(`https://api-crm.zelta.dev/public/v1/products/${id}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
basePrice: '349.00',
description: 'Acceso completo al CRM con hasta 10 usuarios, soporte prioritario y módulo de automatizaciones.'
})
});
const result = await response.json();Ejemplo Py
import requests
product_id = 'qL5oCwDyEzFa'
response = requests.put(
f'https://api-crm.zelta.dev/public/v1/products/{product_id}',
headers={
'Authorization': 'Bearer tu-api-token-aqui',
'Content-Type': 'application/json'
},
json={
'basePrice': '349.00',
'description': 'Acceso completo al CRM con hasta 10 usuarios, soporte prioritario y módulo de automatizaciones.'
}
)
result = response.json()Respuesta exitosa
HTTP 200 OK — Retorna el producto con los campos actualizados.
Eliminar Producto
DELETE /products/:idElimina un producto del catálogo (soft delete). El producto deja de aparecer en listados pero los registros históricos vinculados se conservan.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string | Sí | ID del producto |
Ejemplo
curl -X DELETE "https://api-crm.zelta.dev/public/v1/products/qL5oCwDyEzFa" \
-H "Authorization: Bearer tu-api-token-aqui"Ejemplo JS
const id = 'qL5oCwDyEzFa';
const response = await fetch(`https://api-crm.zelta.dev/public/v1/products/${id}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_TOKEN}`
}
});
const result = await response.json();
console.log(result.success); // trueEjemplo Py
import requests
product_id = 'qL5oCwDyEzFa'
response = requests.delete(
f'https://api-crm.zelta.dev/public/v1/products/{product_id}',
headers={
'Authorization': 'Bearer tu-api-token-aqui'
}
)
result = response.json()
print(result['success']) # TrueRespuesta exitosa
HTTP 200 OK
{
"success": true
}Eliminación y cotizaciones activas
Si el producto está referenciado en cotizaciones, suscripciones u órdenes activas, la eliminación no afecta esos registros existentes. Sin embargo, el producto ya no podrá seleccionarse en nuevos documentos.
Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único del producto (nanoid) |
name | string | Nombre del producto |
type | string | Tipo: physical, service, subscription, digital |
category | string | null | Categoría del producto |
sku | string | null | Código de referencia interno (SKU) |
description | string | null | Descripción completa |
basePrice | string | null | Precio base en formato decimal como string |
currency | string | null | Código de moneda ISO 4217 |
taxRate | string | null | Tasa de impuesto en formato decimal como string |
unit | string | null | Unidad de medida o venta |
status | string | Estado actual: active, archived, draft |
ownerId | string | null | ID del responsable |
ownerName | string | null | Nombre completo del responsable (resuelto por JOIN) |
hasVariants | boolean | Si el producto físico tiene variantes o planes |
metadata | object | Datos personalizados adicionales |
sortOrder | integer | Orden de aparición en listados |
createdAt | string | Fecha de creación (ISO 8601) |
updatedAt | string | Fecha de última actualización (ISO 8601) |
Problemas comunes
| Error | Causa | Solución |
|---|---|---|
401 Unauthorized | Token ausente, inválido o expirado | Verifica tu API token en Configuración > API Keys |
403 Forbidden | Sin permiso products:read/create/update/delete | Revisa los permisos de la API key en tu workspace |
403 Forbidden | Módulo de Productos no activo | Activa el módulo en Configuración > Módulos |
404 Not Found | El producto no existe o fue eliminado | Verifica que el ID sea correcto y pertenezca a tu workspace |
400 — name requerido | Se omitió el nombre en la creación | Incluye un name de hasta 200 caracteres |
400 — type inválido | Se envió un tipo no reconocido | Usa uno de: physical, service, subscription, digital |
400 — status inválido | Se envió un estado no reconocido | Usa uno de: active, archived, draft |
400 — basePrice inválido | Formato de precio incorrecto | Envía el precio como string decimal, ej: "299.00" |
Siguientes pasos
- — Usa productos en cotizaciones para clientes
- — Gestiona suscripciones vinculadas a productos
- — Encuentra productos con el buscador unificado