Saltar al contenido

Productos

Alimenta tu catálogo de servicios y expón inventarios de forma segura en tus frontends.

Descripción General

El objeto Producto contiene los elementos de tu lista de precios (SKU, nombre, precio base y métrica de unidad). La API de productos de Cord es lo suficientemente segura como para que puedas consumirla no solo desde tus servidores internos, sino también directamente desde el navegador de tus clientes.

Crear un Producto

Usa tu Clave de API Secreta (sk_) para alimentar el catálogo masivamente. Ideal para integraciones nocturnas o webhooks provenientes de sistemas de gestión de inventarios.

Petición:

curl -X POST "https://cordhq.app/api/v1/productos" \
     -H "Authorization: Bearer sk_live_tU..." \
     -H "Content-Type: application/json" \
     -d '{
       "sku": "LIC-PRO-24",
       "nombre": "Licencia Anual Profesional",
       "unidad": "licencia",
       "precio": 12000,
       "activo": true
     }'

Respuesta Exitosa:

{
  "data": {
    "id": "7e3c9a1f-2b4d-4c6e-8f0a-b1d3e5f7a9c2"
  }
}

Campos opcionales de impuesto y facturación

Campo Qué es Validación
tax_rate Impuesto que el producto sugiere al agregarlo a una cotización o factura, como fracción (0.08 = 8 %). Sin él, la línea usa tu tasa predeterminada. Debe ser una tasa de tu catálogo de impuestos (o 0); si no, responde 400.
clave_sat Clave de producto o servicio del SAT (c_ClaveProdServ) para el CFDI en México. 8 dígitos (81111500); si no, 400.
clave_unidad_sat Clave de unidad del SAT (c_ClaveUnidad). Sin ella, se deduce de unidad al timbrar (hora → HUR). 1 a 3 letras o números (E48), se guarda en mayúsculas; si no, 400.

En el listado llegan como taxRate, claveSat y claveUnidadSat (null si no se asignaron).

Cada producto trae también moneda: la divisa de precio, costo y los precios por volumen. Es la de tu organización: un producto no tiene divisa propia. Los eventos product.* la traen igual.

Leer, actualizar y borrar un producto

GET /api/v1/productos/{id} devuelve un producto con la misma forma que cada fila del listado. PATCH /api/v1/productos/{id} lo actualiza en parte: cada campo que mandas reemplaza al guardado y el que no mandas se conserva; null en tax_rate, clave_sat, clave_unidad_sat o naturaleza lo quita. Responde el producto actualizado y emite product.updated. Un campo con el tipo equivocado responde 400 con code: "invalid_request" y field (por ejemplo "precio debe ser un número."), sin cambiar nada.

curl -X PATCH "https://cordhq.app/api/v1/productos/7e3c9a1f-2b4d-4c6e-8f0a-b1d3e5f7a9c2" \
     -H "Authorization: Bearer sk_live_tU..." \
     -H "Content-Type: application/json" \
     -H "Idempotency-Key: precio-LIC-PRO-24-2027" \
     -d '{ "precio": 12600, "precios_volumen": [{ "min": 10, "precio": 11800 }] }'

DELETE /api/v1/productos/{id} lo borra y emite product.deleted. Las cotizaciones y facturas que ya lo usan conservan sus líneas tal como se capturaron; solo pierden la liga con el catálogo. Para sacarlo del buscador del editor sin borrarlo, manda "activo": false.

Las tres piden una llave secreta o restringida con permiso sobre productos (lectura para GET, escritura para PATCH y DELETE). Una llave publicable no las usa.

Listar Productos y Seguridad de Datos

La API de listado (GET /api/v1/productos) cuenta con un mecanismo de enmascaramiento dinámico (Dynamic Masking) basado en el nivel de confianza de la clave de API con la que te autenticas.

Si usas una Clave Secreta (sk_): Cord asume que eres un sistema interno confiable y te devuelve el objeto completo, incluyendo los costos ocultos y márgenes de ganancia (costo).

Si usas una Clave Publicable (pk_): Las claves publicables se exponen en el navegador (por ejemplo, para pintar una lista de precios en una web pública de React). Cord asume riesgo y retira automáticamente el campo costo de todos los resultados en memoria antes de serializar el JSON. Así, tus márgenes jamás se fugan al frontend público.

curl -X GET "https://cordhq.app/api/v1/productos?limit=50" \
     -H "Authorization: Bearer pk_live_yX..."

Con sk_live_... el mismo producto incluye costo:

{ "data": [ { "id": "7e3c9a1f-2b4d-4c6e-8f0a-b1d3e5f7a9c2", "sku": "LIC-PRO-24", "nombre": "Licencia Anual Profesional", "unidad": "licencia", "precio": 12000, "costo": 4000, "moneda": "MXN", "activo": true, "taxRate": null, "claveSat": "43231500", "claveUnidadSat": null } ] }

Con pk_live_..., costo simplemente no aparece en el objeto (no viene como null ni 0 — la clave no existe):

{ "data": [ { "id": "7e3c9a1f-2b4d-4c6e-8f0a-b1d3e5f7a9c2", "sku": "LIC-PRO-24", "nombre": "Licencia Anual Profesional", "unidad": "licencia", "precio": 12000, "moneda": "MXN", "activo": true, "taxRate": null, "claveSat": "43231500", "claveUnidadSat": null } ] }

Nota: una Publishable Key solo puede usarse en cuatro rutas: GET /api/v1/productos, POST /api/v1/cotizaciones, GET /api/v1/elements/config y POST /api/v1/elements/ai-draft. Cualquier otra ruta (crear productos, leer clientes, cobranza) responde 403 insufficient_scope. Además exige un header Origin o Referer que coincida con los dominios permitidos de tu organización, que se configuran en Ajustes › Cotizador embebible. Una pk_live_ sin dominios configurados responde 403 origin_allowlist_required; una pk_test_ funciona desde cualquier origen.