Crear, actualizar y borrar productos

Estos endpoints cubren el ciclo de vida de escritura de un producto: creación, actualización (parcial o completa) y borrado. El renombrado de SKU no tiene endpoint propio — se dispara implícitamente al actualizar. El borrado es permanente.

Permisos requeridos: products:create para crear, products:update para actualizar, products:delete para borrar.

Crear un producto

Crea un nuevo producto en el tenant autenticado

skustringrequired

SKU único dentro del tenant. Se normaliza a MAYÚSCULAS al guardar.

titleobjectrequired

{ value: string, translations?: [] } — value máximo 500 caracteres.

pricenumberrequired

Precio base, mínimo 0.

currencystringrequired

Código de moneda ISO 4217.

statusstringrequired

active, disabled o draft.

compareAtPricenumber

Precio de comparación. Si lo envías, debe ser MAYOR que price — si no, el guardado falla.

productTypestring

physical, digital, service, bundle, supply o material. Ver la nota sobre type vs productType en el modelo de datos.

variantsarray

Arreglo de variantes. Máximo 100 en una actualización.

mediaarray

Arreglo de imágenes/media. Ver Variantes y media.

bindingsarray

Vínculos a canales de venta. Máximo 50 en una actualización.

{
  "sku": "CAM-ROJO-M",
  "title": { "value": "Camisa Roja Talla M" },
  "price": 499.00,
  "currency": "MXN",
  "status": "active",
  "productType": "physical"
}
201
{
  "id": "65f3a1b2c4d5e6f7a8b9c0e1",
  "sku": "CAM-ROJO-M",
  "status": "active",
  "productType": "physical",
  "title": { "value": "Camisa Roja Talla M", "translations": [] },
  "price": 499.00,
  "currency": "MXN",
  "taxable": true,
  "media": [],
  "variants": [],
  "bindings": []
}
409
{
  "code": "duplicate-sku",
  "message": "Product with SKU 'CAM-ROJO-M' already exists",
  "field": "sku"
}

Permiso requerido: products:create

Campos requeridos

CampoTipoDescripción
skustringMáximo 100 caracteres. Único por tenant (índice compuesto tenantId + sku).
title.valuestringMáximo 500 caracteres.
pricenumberMínimo 0.
currencystringCódigo ISO 4217.
statusstringactive, disabled o draft — ver la nota sobre el enum real abajo.

status real tiene 3 valores, no 5

El schema persistido (Mongoose) solo acepta active, disabled y draft, con default draft. Si tu integración proviene de otra fuente que asume los 5 valores del tipo TS público (active/disabled/inactive/draft/out_of_stock), ten cuidado: inactive y out_of_stock son rechazados al guardar.

Ejemplo

curl -X POST https://api.fenicia.io/products \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "CAM-ROJO-M",
    "title": { "value": "Camisa Roja Talla M" },
    "price": 499.00,
    "currency": "MXN",
    "status": "active",
    "productType": "physical"
  }'

Actualizar un producto

Actualiza un producto existente, parcial o completamente

idstringrequired

_id o SKU del producto a actualizar.

{
  "price": 549.00,
  "status": "active"
}
200
{
  "id": "65f3a1b2c4d5e6f7a8b9c0e1",
  "sku": "CAM-ROJO-M",
  "status": "active",
  "price": 549.00,
  "currency": "MXN",
  "...": "resto de campos del producto actualizado"
}
404
{ "code": "not-found", "message": "Product not found" }
409
{ "code": "duplicate-sku", "message": "Product with SKU 'CAM-ROJO-MED' already exists", "field": "sku" }

Permiso requerido: products:update

El cuerpo acepta cualquier subconjunto de los campos del producto — no es necesario reenviar el objeto completo. Dos comportamientos especiales de la librería de validación:

  • compareAtPrice: null se interpreta como "limpiar el campo" de forma explícita, distinto de simplemente omitirlo.
  • Límites de tamaño en arreglos: variants.length ≤ 100, bindings.length ≤ 50.

También existe una variante legada: PUT /products (sin {id} en la ruta, con el SKU dentro del body). Mismo permiso y mismo comportamiento — se documenta aquí por completitud, pero usa PUT /products/{id} en integraciones nuevas.

El rename de SKU es implícito

No existe un endpoint dedicado para renombrar un SKU

Para cambiar el SKU de un producto, envía el nuevo valor en el campo sku del body de PUT /products/{id}. Si body.sku difiere del SKU almacenado, el lambda dispara internamente el rename en cascada — no hay una ruta separada como POST /products/{id}/rename.

El rename en cascada actualiza, en la misma operación: los registros de inventario asociados, inventory_tracks, las referencias desde BOM/bundle/modificadores que apunten al SKU viejo, y emite el evento ProductSkuChanged. Trátalo como una operación de mayor impacto que una actualización de campo simple — verifica que el nuevo SKU no colisione con uno existente (responderá 409 duplicate-sku si lo hace).

curl -X PUT https://api.fenicia.io/products/CAM-ROJO-M \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "sku": "CAM-ROJO-MED" }'

Borrar un producto

Elimina permanentemente un producto

idstringrequired

_id o SKU del producto a borrar.

200
{ "success": true, "message": "Product deleted" }
404
{ "code": "not-found", "message": "Product not found" }

Permiso requerido: products:delete

Hard delete irreversible — sin papelera, sin soft-delete

Este endpoint ejecuta un deleteOne directo sobre la colección de MongoDB. No hay papelera de reciclaje ni estado recuperable. Una vez borrado, el producto y su historial dejan de existir en la base de datos — no puedes restaurarlo desde la API.

Esto es distinto de poner status: 'disabled', que es un estado lógico reversible (el producto sigue existiendo, solo se oculta/desactiva). Si tu flujo necesita la posibilidad de "deshacer", usa PUT /products/{id} con {"status": "disabled"} en lugar de DELETE.

curl -X DELETE https://api.fenicia.io/products/CAM-ROJO-M \
  -H "Authorization: Bearer fkapi_tu_api_key"

Errores

CódigoStatusDescripción
bad-request/missing-body400El request no incluyó cuerpo.
bad-request/invalid-json400El cuerpo no es JSON válido.
validation-error400/409El producto no cumple las reglas de validación. Incluye fieldErrors[] con el detalle por campo.
duplicate-sku409Ya existe un producto con ese SKU en el tenant (al crear, o al renombrar hacia un SKU ocupado).
not-found404No existe un producto que resuelva el id/SKU indicado (en PUT, solo cuando el identificador es un _id que no resuelve; en DELETE, siempre que no exista).

Los códigos de autenticación (token inválido, permiso faltante) son los mismos en toda la plataforma — consulta el catálogo completo de errores.

Siguientes pasos