Actualizado el 18-oct-2026. Primera versión de la arquitectura de esta épica.
Qué responde. Cómo funciona por dentro FT-PRD-01: qué piezas participan, en qué orden hablan, qué APIs hay que construir con su request y su response, y qué le entrega a la épica siguiente.
Para quién. Los equipos que van a construir la épica, QA y arquitectura.
Estado. [Propuesta] del TL del Nuevo PIM, 18-oct-2026, construida sobre ADR-12. La spec de cada épica dice el qué; estas páginas, el cómo.
Fuente. docs/arquitectura/por-epica/epicas/FT-PRD-01.json, generada con generar.py; la ficha de la épica es trazabilidad/features/FT-PRD-01-modelo-canonico-de-producto.md.
En 30 segundos
Todo producto del PIM se guarda igual, venga de donde venga: una familia (genérico, individual, set o look) con sus variantes adentro, un tipo explícito, llaves que no se repiten (SKU inmutable y EAN único por negocio), relaciones de ida y vuelta y banderas con vigencia. Esta épica fija ese modelo y las APIs para crear, leer, ligar y comparar productos, para que el portal, Foro, SAP, la tienda y la migración vean la misma ficha.
- Módulo dueño: MS-02 Producto. También participan: MS-01 Catálogo, MS-03 Migración y convivencia.
- Etapa T · Transversal. Recibe «PE-C · Catálogo vigente», «PE-T · Usuario con permisos y bitácora» y entrega «PE-1 · Propuesta en Borrador».
- APIs: construye 17 y usa 6 que construyen otras épicas.
- Eventos: publica 5 y consume 1.
1. Dónde encaja
Recibe
| De | Qué |
|---|---|
| PE-C · Catálogo vigente | Las plantillas y su vista efectiva en Catálogo, para tipar y validar cada valor y saber qué hereda cada variante. |
| PE-T · Usuario con permisos y bitácora | Quien crea o cambia un producto ya se identificó y tiene permiso; cada cambio queda en la bitácora. |
Entrega
| A | Qué |
|---|---|
| PE-1 · Propuesta en Borrador | El documento de la familia en products, en Borrador, con negocio, tipo, origen y dueño, venga de la puerta que venga. |
| FT-PRD-03 · EAN | El registro de identificadores por negocio, donde cada EAN se reserva en la misma transacción que el producto. |
| FT-PRD-02 · Creación de SKU con SAP | Las variantes con identificador propio, listas para recibir el SKU que manda SAP y que después nadie cambia. |
| FT-MIG-03 · Migración inicial de productos desde P360 | La forma del documento, las reglas de integridad y la derivación del tipo con que se carga lo migrado. |
| FT-INT-01 · Publicación a e-commerce | La familia con relaciones, banderas con vigencia y variantes publicables, de donde sale la foto para la tienda. |
| FT-ARQ-01 · Núcleo headless | El modelo canónico y sus APIs de alta y lectura, sobre los que se arma la API única de producto para cualquier origen. |
| FT-FLU-01 · Motor de estados configurable con dos carreteras y reglas… | El documento del producto en products, donde viven el estado, las tareas abiertas y los rechazos que mueve el motor. |
| FT-ACT-04 · Variantes a partir de imágenes y herencia de imágenes por… | La familia con sus variantes y el color de cada una, que es la llave de la herencia. |
| FT-RAC-07 · Reportes, exportes, importes y búsquedas por atributos | El modelo de producto que se exporta: familia, variantes, valores por código, clasificaciones y dueño. |
| FT-REF-03 · Servicio de matching | El modelo de producto y el registro de EAN por negocio contra los que se busca, servidos por MS-02. |
Cambios de estado que hace
| Transición | De → a | Cuándo |
|---|---|---|
| T01 | Inicio → Borrador | El proveedor o seller guarda por primera vez (pantalla, archivo, plantilla en línea o API). |
2. Diagrama de contexto
Quién participa y cómo se conecta. Las pantallas llegan por el BFF del portal, que valida el token y compone; cada módulo vuelve a validar el token y decide sus permisos con los roles de MS-13.
🟩 Verde: nuevo, se construye · 🟦 Azul: ya existe y se queda, se reutiliza su código · 🟧 Naranja: temporal o se retira (se apaga en R3) · ⬜ Gris: sistema externo · ★ módulo dueño
3. Diagrama de flujo
El camino principal, paso por paso. Cuando una pantalla llama a un módulo se ven dos flechas: la pantalla al BFF y el BFF al módulo. Los eventos salen del outbox del módulo después de guardar; quien los consume ignora los repetidos.
Los pasos en texto
| Paso | De | A | Qué hace | API o evento |
|---|---|---|---|---|
| 1 | Proveedor | Entrada Única · Vendor Center | Inicia el alta de un producto nuevo | — |
| 2 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Pide el plan del alta con negocio, tipo y dueño | API-02-005 |
| 3 | MS-02 Producto | MS-01 Catálogo | Si ya trae plantilla, lee su vista efectiva | API-01-028 |
| 4 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Confirma el alta | API-02-057 |
| 5 | MS-02 Producto | Base pim | Guarda la familia en Borrador y reserva sus llaves | — |
| 6 | MS-02 Producto | Eventos | Avisa que nació el producto | catalog.product.changed/PRODUCT_CREATED |
| 7 | Gobierno de Datos | Entrada Única · Vendor Center | Abre la ficha de la familia | — |
| 8 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Lee la familia completa como una unidad | API-02-002 |
| 9 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Lee una variante con lo heredado marcado | API-02-004 |
| 10 | Gobierno de Datos | Entrada Única · Vendor Center | Captura un modelo propio en esa variante | — |
| 11 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Pide el plan del valor propio de la variante | API-02-012 |
| 12 | Entrada Única · Vendor Center | MS-02 Producto (por el BFF) | Confirma; las hermanas siguen heredando del genérico | API-02-057 |
| 13 | MS-02 Producto | Eventos | Avisa el cambio de la variante | catalog.product.changed/PRODUCT_UPDATED |
3.1 Una variante que no procede
3.2 Relaciones y banderas con vigencia
3.3 Producto migrado sin tipo
4. Si algo sale mal
- Alguien intenta cambiar el SKU de una variante. El plan responde 422: el SKU sólo lo escribe la respuesta de SAP (FT-PRD-02) y es inmutable. Quien lo intentó ve el motivo; no se guarda nada.
- Dos altas guardan el mismo EAN en el mismo negocio al mismo tiempo. El índice único del registro de identificadores deja pasar sólo la primera; la segunda recibe 422 con «El código EAN ya existe en catalogación para este negocio» y la opción de pedir su liberación (FT-PRD-03).
- Un producto migrado no tiene tipo o negocio. No entra al hilo: Migración lo deja en cuarentena con su motivo y Gobierno de Datos lo corrige desde la lista; una familia en cuarentena no se publica ni se ofrece como aprobable.
- Se agrega un producto a un segundo grupo de venta cruzada. El plan lo marca bloqueado con el grupo donde ya está; se aplica sólo a los miembros válidos y Category ve cuál quedó fuera.
- Dos personas cambian la misma familia a la vez. La segunda confirmación responde 409 VERSION_CONFLICT; el portal recarga la ficha y la persona decide sobre la versión nueva.
5. APIs
Las que construye esta épica (17)
POST /api/v2/products/plans · Alta de un producto
| Campo | Valor |
|---|---|
| API | API-02-005 · MS-02 Producto |
| Para qué | Prepara el alta de un producto en Borrador desde cualquier puerta (pantalla, archivo o API) con su negocio, tipo, origen, dueño, datos de identificación y la evaluación de la identificación (evaluationId), y dice qué va a crear y qué EAN reservará, sin escribir nada. Con parentId agrega una variante a una familia que ya existe. |
| En esta épica | Preparar el alta de un producto en Borrador desde cualquier puerta |
| Quién la llama | BFF del portal, Gateway de terceros /partner/v1 |
| Permiso | ENT-E; el proveedor o seller sólo para sí mismo |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Publica | catalog.product.changed/PRODUCT_CREATED |
| También la usan | FT-ARQ-01, FT-FLU-02, FT-FLU-03, FT-PRD-09, FT-REF-02, FT-REF-03, FT-REF-04 |
Request
{
"business": "MER",
"type": "GENERIC",
"origin": "PORTAL",
"ownerId": "PROV-0001",
"name": "Licuadora de 10 velocidades",
"identification": {
"brand": "MARCA01",
"model": "LX-200",
"eans": [
"7501234567893"
]
},
"evaluationId": "evl-5d21c8",
"templateId": null,
"parentId": null
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-a41c07",
"expiresAt": "2026-10-18T16:40:00Z",
"willCreate": {
"type": "GENERIC",
"business": "MER",
"status": "BORRADOR",
"variants": 0
},
"eans": [
{
"ean": "7501234567893",
"type": "HE",
"result": "RESERVE"
}
],
"warnings": [
{
"code": "NO_VARIANTS_YET",
"message": "Un genérico puede guardarse sin variantes; para enviarlo necesitará al menos una"
}
],
"applyUrl": "/api/v2/product-plans/pln-a41c07/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 422 | VALIDATION_ERROR | El tipo no corresponde al negocio (por ejemplo, GENERIC_MKP en Mercaderías) o falta el dueño |
| 422 | BUSINESS_ERROR | Un EAN ya existe en catalogación para ese negocio |
| 403 | AUTHORIZATION_ERROR | Quien llama no puede crear productos para ese dueño o negocio |
GET /api/v2/products/{id} · Ficha del producto
| Campo | Valor |
|---|---|
| API | API-02-002 · MS-02 Producto |
| Para qué | Devuelve la familia completa como una unidad: tipo, negocio, dueño, estado y tareas abiertas, plantilla y categorías, textos con su origen, banderas, relaciones, completitud y el resumen de sus variantes. Con view se pide sólo una parte (overview, attributes, texts, images, classification). |
| En esta épica | Leer la familia completa como una unidad |
| Quién la llama | BFF del portal, Gateway de terceros /partner/v1 |
| Permiso | ENT-L; el proveedor o seller dueño |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | id = PRD-0004812, view = overview |
| También la usan | FT-ACT-01, FT-INT-06, FT-PRD-04, FT-PRD-09, FT-PRD-10, FT-RAC-03, FT-USR-02 |
Request
null
Response · 200
{
"success": true,
"data": {
"productId": "PRD-0004812",
"type": "GENERIC",
"business": "MER",
"origin": "PORTAL",
"ownerId": "PROV-0001",
"version": 7,
"status": {
"code": "BORRADOR",
"since": "2026-10-16T18:02:00Z"
},
"openTasks": [],
"classification": {
"templateId": "EU4-0412",
"webCategoryIds": [],
"autoAssignedWebCategoryIds": []
},
"master": {
"brand": "MARCA01",
"supplierId": "PROV-0001",
"supplierPartNumber": "LX-200",
"itemGroup": null
},
"texts": {
"productName": "Licuadora MARCA01 LX-200",
"productNameSource": "RULE",
"titleWithoutBrand": "Licuadora LX-200",
"descriptionLong": null
},
"flags": [
{
"code": "PRESALE",
"value": true,
"from": "2026-11-01T06:00:00Z",
"to": "2026-11-30T06:00:00Z"
}
],
"relations": {
"crossSellGroupId": null,
"configurableGroupId": null
},
"completeness": {
"mandatory": {
"present": 18,
"total": 22,
"status": "MEASURED"
}
},
"variants": [
{
"variantId": "VAR-0004812-01",
"sku": null,
"ean": "7501234567893",
"axes": {
"COLOR": "NEGRO"
},
"publishable": true
},
{
"variantId": "VAR-0004812-02",
"sku": null,
"ean": "7501234567909",
"axes": {
"COLOR": "ROJO"
},
"publishable": true
}
]
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El producto no existe o quien llama no lo puede ver |
GET /api/v2/products/{id}/variants · Variantes del producto
| Campo | Valor |
|---|---|
| API | API-02-003 · MS-02 Producto |
| Para qué | Lista las variantes de una familia con su SKU, EAN y tipo de código, sus ejes (talla, color), si tiene imagen, si se puede publicar y cuántos valores hereda del genérico. |
| En esta épica | Listar las variantes con SKU, EAN y ejes |
| Quién la llama | BFF del portal, Gateway de terceros /partner/v1 |
| Permiso | ENT-L; el proveedor o seller dueño |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | id = PRD-0004812 |
| También la usan | FT-ACT-04 |
Request
null
Response · 200
{
"success": true,
"data": {
"items": [
{
"variantId": "VAR-0004812-01",
"sku": "1000004812",
"ean": "7501234567893",
"eanType": "HE",
"axes": {
"COLOR": "NEGRO"
},
"hasImage": true,
"publishable": true,
"inheritedValues": 14,
"ownValues": 3
},
{
"variantId": "VAR-0004812-02",
"sku": "1000004813",
"ean": "7501234567909",
"eanType": "HE",
"axes": {
"COLOR": "ROJO"
},
"hasImage": false,
"publishable": false,
"notPublishableReason": "NO_PROCEDE",
"inheritedValues": 14,
"ownValues": 4
}
],
"page": 1,
"size": 50,
"total": 2
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El producto no existe o quien llama no lo puede ver |
GET /api/v2/products/{id}/variants/{variantId} · Una variante con lo propio y lo heredado
| Campo | Valor |
|---|---|
| API | API-02-004 · MS-02 Producto |
| Para qué | Devuelve una variante con cada valor marcado como heredado del genérico o propio; lo propio puede ser un eje (talla, color) o un valor que sobrescribe al genérico (modelo, precio, costo). |
| En esta épica | Leer una variante con lo propio y lo heredado |
| Quién la llama | BFF del portal, Gateway de terceros /partner/v1 |
| Permiso | ENT-L; el proveedor o seller dueño |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | id = PRD-0004812, variantId = VAR-0004812-02 |
Request
null
Response · 200
{
"success": true,
"data": {
"variantId": "VAR-0004812-02",
"productId": "PRD-0004812",
"sku": "1000004813",
"ean": "7501234567909",
"version": 7,
"values": [
{
"attributeId": "MARCA",
"value": {
"code": "MARCA01"
},
"inherited": true,
"from": "PRD-0004812"
},
{
"attributeId": "PROVEEDOR",
"value": {
"code": "PROV-0001"
},
"inherited": true,
"from": "PRD-0004812"
},
{
"attributeId": "MODELO_COMERCIAL",
"value": {
"text": "LX-200-R"
},
"inherited": false,
"overridesParent": true
},
{
"attributeId": "COLOR",
"value": {
"code": "ROJO"
},
"inherited": false,
"axis": true
}
],
"publishable": true
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | La variante no existe o no pertenece a ese producto |
POST /api/v2/products/{id}/root/plans · Cambiar datos generales
| Campo | Valor |
|---|---|
| API | API-02-011 · MS-02 Producto |
| Para qué | Prepara un cambio a los datos generales de un producto o de una de sus variantes (número de parte, banderas con vigencia, la marca de «no procede» de una variante) y muestra el antes y el después. SKU, EAN y negocio son de sólo lectura aquí. |
| En esta épica | Cambiar datos generales, banderas con vigencia y la marca «no procede» |
| Quién la llama | BFF del portal |
| Permiso | ENT-E; la marca «no procede» sólo QA |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key, If-Match |
| Parámetros | id = PRD-0004812 |
| Publica | catalog.product.changed/PRODUCT_UPDATED, catalog.product.publication/PUBLICATION_REQUESTED |
| También la usan | FT-PRD-10 |
Request
{
"fields": {
"flags": [
{
"code": "PRESALE",
"value": true,
"from": "2026-11-01T06:00:00Z",
"to": "2026-11-30T06:00:00Z"
}
]
}
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-61b7d0",
"expiresAt": "2026-10-18T16:50:00Z",
"changes": [
{
"path": "flags.PRESALE",
"before": null,
"after": {
"value": true,
"from": "2026-11-01T06:00:00Z",
"to": "2026-11-30T06:00:00Z"
}
}
],
"republish": false,
"applyUrl": "/api/v2/product-plans/pln-61b7d0/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 422 | VALIDATION_ERROR | Se intenta cambiar el SKU, el EAN o el negocio (el EAN va por su flujo y el negocio por su comando) |
| 422 | VALIDATION_ERROR | Una bandera sin fecha de fin o con el fin antes del inicio |
| 409 | VERSION_CONFLICT | Alguien cambió el producto después de abrirlo |
POST /api/v2/product-groups/plans · Grupos de productos
| Campo | Valor |
|---|---|
| API | API-02-106 · MS-02 Producto |
| Para qué | Prepara crear un grupo (venta cruzada, configurable o look) o agregar y quitar miembros; revisa que cada producto esté en un solo grupo de cada tipo y dice a quiénes habrá que republicar. |
| En esta épica | Crear grupos de venta cruzada, configurables y looks, y cambiar sus miembros |
| Quién la llama | BFF del portal |
| Permiso | ENT-E (Category y Gobierno de Producto) |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Publica | catalog.product.changed/PRODUCT_UPDATED, catalog.product.publication/PUBLICATION_REQUESTED |
| También la usan | FT-PLT-08, FT-PRD-09 |
Request
{
"action": "ADD_MEMBERS",
"groupId": "CROSS_SELL::CS-0091",
"type": "CROSS_SELL",
"members": [
"PRD-0004812",
"PRD-0004990"
]
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-77d0e3",
"expiresAt": "2026-10-18T17:25:00Z",
"group": {
"groupId": "CROSS_SELL::CS-0091",
"type": "CROSS_SELL",
"membersBefore": 3,
"membersAfter": 4
},
"items": [
{
"productId": "PRD-0004812",
"result": "OK"
},
{
"productId": "PRD-0004990",
"result": "BLOCKED",
"reason": "Ya pertenece a CROSS_SELL::CS-0050: un producto sólo está en un grupo de venta cruzada"
}
],
"republish": [
"PRD-0004812"
],
"applyUrl": "/api/v2/product-plans/pln-77d0e3/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El grupo o algún miembro no existe |
| 422 | BUSINESS_ERROR | Todos los miembros quedan bloqueados: no hay nada que aplicar |
GET /api/v2/product-groups/{groupId} · Leer un grupo de productos
| Campo | Valor |
|---|---|
| API | API-02-107 · MS-02 Producto |
| Para qué | Devuelve un grupo de venta cruzada, configurable o look con sus miembros y, si es configurable, el orden de su configuración. |
| En esta épica | Leer un grupo con sus miembros |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | groupId = CONFIGURABLE::PRD-0006001 |
Request
null
Response · 200
{
"success": true,
"data": {
"groupId": "CONFIGURABLE::PRD-0006001",
"type": "CONFIGURABLE",
"business": "MER",
"configurableOrder": [
"CAPACIDAD",
"COLOR"
],
"members": [
{
"productId": "PRD-0006002",
"name": "Batidora MARCA01 BX-5 de 1.5 L negra"
},
{
"productId": "PRD-0006003",
"name": "Batidora MARCA01 BX-5 de 2 L negra"
}
],
"version": 4
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El grupo no existe |
POST /api/v2/products/{id}/variants/link/plans · Ligar una variante a su genérico
| Campo | Valor |
|---|---|
| API | API-02-014 · MS-02 Producto |
| Para qué | Prepara ligar a esta familia una variante que hoy está suelta, y dice qué empieza a heredar del genérico y qué cambia en los dos productos. |
| En esta épica | Ligar una variante suelta a su genérico |
| Quién la llama | BFF del portal |
| Permiso | ENT-E |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Parámetros | id = PRD-0004812 |
| Publica | catalog.product.changed/VARIANT_LINKED |
| También la usan | FT-ACT-04, FT-RAC-02, FT-REF-02 |
Request
{
"variantId": "VAR-0005120-01",
"versions": {
"PRD-0004812": 7,
"PRD-0005120": 2
}
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-3e8a21",
"expiresAt": "2026-10-18T17:00:00Z",
"from": "PRD-0005120",
"to": "PRD-0004812",
"inheritsFromNewParent": [
"MARCA",
"PROVEEDOR"
],
"applyUrl": "/api/v2/product-plans/pln-3e8a21/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 422 | BUSINESS_ERROR | La variante ya pertenece a otro genérico: primero se desliga |
| 422 | VALIDATION_ERROR | La variante tiene otra plantilla u otro negocio que el genérico |
POST /api/v2/products/{id}/variants/unlink/plans · Desligar una variante
| Campo | Valor |
|---|---|
| API | API-02-015 · MS-02 Producto |
| Para qué | Prepara sacar una variante de su familia: queda como producto individual con el mismo identificador y su SKU, y conserva su historial. |
| En esta épica | Desligar una variante y dejarla como individual |
| Quién la llama | BFF del portal |
| Permiso | ENT-E |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Parámetros | id = PRD-0004812 |
| Publica | catalog.product.changed/VARIANT_UNLINKED |
Request
{
"variantId": "VAR-0004812-02",
"version": 7
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-91c4f2",
"expiresAt": "2026-10-18T17:00:00Z",
"variantId": "VAR-0004812-02",
"becomes": {
"type": "INDIVIDUAL",
"keepsSku": "1000004813"
},
"applyUrl": "/api/v2/product-plans/pln-91c4f2/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 422 | BUSINESS_ERROR | Es la última variante de un genérico ya enviado: un genérico enviado no se queda sin variantes |
POST /api/v2/products/{id}/clone/plans · Clonar un producto
| Campo | Valor |
|---|---|
| API | API-02-017 · MS-02 Producto |
| Para qué | Prepara una propuesta nueva en Borrador copiada de otro producto, sin identificador, SKU, EAN, estados, tareas, historial ni categorías, para no capturar de cero algo parecido. |
| En esta épica | Clonar un producto como propuesta nueva sin llaves ni estados |
| Quién la llama | BFF del portal |
| Permiso | ENT-E y lectura del producto de origen |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Parámetros | id = PRD-0004812 |
| Publica | catalog.product.changed/PRODUCT_CREATED |
Request
{
"business": "MER",
"ownerId": "PROV-0001",
"copy": [
"attributes",
"texts",
"classification.templateId"
]
}
Response · 200
{
"success": true,
"data": {
"planId": "pln-d40e66",
"expiresAt": "2026-10-18T17:10:00Z",
"willCreate": {
"type": "GENERIC",
"status": "BORRADOR",
"variants": 0
},
"notCopied": [
"sku",
"ean",
"status",
"tasks",
"history",
"webCategoryIds"
],
"applyUrl": "/api/v2/product-plans/pln-d40e66/apply"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El producto de origen no existe |
| 403 | AUTHORIZATION_ERROR | Quien llama no puede ver el producto de origen |
POST /api/v2/products/search · Buscar productos
| Campo | Valor |
|---|---|
| API | API-02-001 · MS-02 Producto |
| Para qué | Busca productos y propuestas con filtros combinados (negocio, estado, plantilla, categoría, dueño, con o sin variantes, valor de un atributo) y cuenta por valor; sirve a la consulta, a las bandejas y a las correcciones masivas. |
| En esta épica | Buscar productos con filtros combinados |
| Quién la llama | BFF del portal, Gateway de terceros /partner/v1 |
| Permiso | ENT-L; el proveedor o seller sólo ve sus productos |
| Cabeceras | Authorization, X-Request-Id |
| También la usan | FT-ARQ-01, FT-CAT-04, FT-CAT-05, FT-FLU-04, FT-FLU-06, FT-PLT-03, FT-PRD-04, FT-PRD-05, FT-PRD-09, FT-RAC-07, FT-USR-01, FT-USR-02 |
Request
{
"filters": [
{
"field": "business",
"op": "EQ",
"value": "MKP"
},
{
"field": "status",
"op": "IN",
"values": [
"APROBADO"
]
},
{
"field": "classification.webCategoryIds",
"op": "IS_EMPTY"
}
],
"page": 1,
"size": 50,
"facets": [
"ownerId"
]
}
Response · 200
{
"success": true,
"data": {
"items": [
{
"productId": "PRD-0004812",
"type": "GENERIC",
"business": "MKP",
"name": "Playera MARCA01 deportiva de algodón",
"status": "APROBADO",
"templateId": "EU4-0412",
"ownerId": "SELLER-0001",
"variantCount": 6,
"webCategoryIds": [],
"updatedAt": "2026-10-17T21:40:00Z"
}
],
"page": 1,
"size": 50,
"total": 23104,
"facets": {
"ownerId": [
{
"value": "SELLER-0001",
"count": 482
}
]
}
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 400 | VALIDATION_ERROR | Un filtro usa un campo u operador que no existe |
| 400 | VALIDATION_ERROR | La lista de IDs pasa de 10,000 sin venir como archivo; se rechaza completa, nunca se trunca |
GET /api/v2/products/compare?ids= · Comparar productos
| Campo | Valor |
|---|---|
| API | API-02-018 · MS-02 Producto |
| Para qué | Compara lado a lado de 2 a 5 productos y devuelve sólo los campos donde difieren. |
| En esta épica | Comparar productos lado a lado |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | ids = PRD-0004812,PRD-0004990 |
Request
null
Response · 200
{
"success": true,
"data": {
"items": [
{
"productId": "PRD-0004812"
},
{
"productId": "PRD-0004990"
}
],
"differences": [
{
"path": "attributes.CAPACIDAD",
"values": {
"PRD-0004812": "1.5 L",
"PRD-0004990": "2 L"
}
}
],
"same": 41
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 400 | VALIDATION_ERROR | Menos de 2 o más de 5 productos |
| 404 | NOT_FOUND | Algún producto no existe o no es visible para quien llama |
GET /api/v2/products/{id}/compare-previous · Comparar con la versión anterior
| Campo | Valor |
|---|---|
| API | API-02-019 · MS-02 Producto |
| Para qué | Compara el producto con su versión anterior o con la última aprobada y lista cada campo que cambió, con el antes y el después. |
| En esta épica | Comparar con la versión anterior o la última aprobada |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | id = PRD-0004812, against = LAST_APPROVED |
Request
null
Response · 200
{
"success": true,
"data": {
"productId": "PRD-0004812",
"current": 12,
"previous": 9,
"previousReason": "APPROVAL",
"changes": [
{
"path": "texts.titleWithoutBrand",
"before": "Licuadora MARCA01 LX-200",
"after": "Licuadora LX-200"
}
]
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El producto no existe o no tiene versión anterior con qué comparar |
GET /api/v2/products/{id}/media · Activos ligados al producto
| Campo | Valor |
|---|---|
| API | API-02-020 · MS-02 Producto |
| Para qué | Devuelve, por tipo y posición, qué activos tiene ligados el producto y cada variante, con su juego de trabajo y su juego congelado para publicar. El archivo y su validación técnica son de Activos digitales (API-11-007). |
| En esta épica | Ver qué activos tiene ligados el producto, por tipo y posición |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | id = PRD-0004812 |
| También la usan | FT-ACT-02 |
Request
null
Response · 200
{
"success": true,
"data": {
"productId": "PRD-0004812",
"media": [
{
"owner": "PRD-0004812",
"type": "PRINCIPAL",
"position": 1,
"assetId": "ast-7710",
"isMain": true,
"working": {
"name": "PRD-0004812_1.jpg"
},
"frozen": {
"name": "PRD-0004812_1_f.jpg",
"frozenAt": "2026-10-12T15:00:00Z"
}
},
{
"owner": "VAR-0004812-02",
"type": "DETALLE",
"position": 2,
"assetId": "ast-7712",
"inheritedByColor": "ROJO",
"working": {
"name": "VAR-0004812-02_2.jpg"
},
"frozen": null
}
]
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 404 | NOT_FOUND | El producto no existe o no es visible para quien llama |
GET /api/v2/saved-searches · Mis búsquedas guardadas
| Campo | Valor |
|---|---|
| API | API-02-022 · MS-02 Producto |
| Para qué | Lista las búsquedas de productos que guardó quien llama y las que le compartieron. |
| En esta épica | Ver las búsquedas guardadas propias y compartidas |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id |
| Parámetros | page = 1, size = 50 |
Request
null
Response · 200
{
"success": true,
"data": {
"items": [
{
"searchId": "srh-0042",
"name": "Marketplace sin categoría",
"filters": [
{
"field": "business",
"op": "EQ",
"value": "MKP"
},
{
"field": "classification.webCategoryIds",
"op": "IS_EMPTY"
}
],
"sharedWith": [
"AREA:GOBIERNO-DATOS"
],
"isOwner": true
}
],
"page": 1,
"size": 50,
"total": 1
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 403 | AUTHORIZATION_ERROR | Quien llama no tiene lectura de productos |
POST /api/v2/saved-searches · Guardar una búsqueda
| Campo | Valor |
|---|---|
| API | API-02-023 · MS-02 Producto |
| Para qué | Guarda una búsqueda de productos con su nombre y con quién se comparte (por área). Necesita la colección saved_searches, que pide FT-PRD-01. |
| En esta épica | Guardar y compartir una búsqueda |
| Quién la llama | BFF del portal |
| Permiso | ENT-L |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
Request
{
"name": "Marketplace sin categoría",
"filters": [
{
"field": "business",
"op": "EQ",
"value": "MKP"
},
{
"field": "classification.webCategoryIds",
"op": "IS_EMPTY"
}
],
"sharedWith": [
"AREA:GOBIERNO-DATOS"
]
}
Response · 201
{
"success": true,
"data": {
"searchId": "srh-0042"
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 400 | VALIDATION_ERROR | Un filtro no es válido o el nombre ya existe para ese usuario |
| 403 | AUTHORIZATION_ERROR | Se comparte con un área a la que quien llama no pertenece |
DELETE /api/v2/saved-searches/{id} · Borrar una búsqueda guardada
| Campo | Valor |
|---|---|
| API | API-02-024 · MS-02 Producto |
| Para qué | Borra una búsqueda guardada; sólo su dueño puede hacerlo. |
| En esta épica | Borrar una búsqueda guardada |
| Quién la llama | BFF del portal |
| Permiso | El dueño de la búsqueda |
| Cabeceras | Authorization, X-Request-Id, Idempotency-Key |
| Parámetros | id = srh-0042 |
Request
null
Response · 200
{
"success": true,
"data": {
"searchId": "srh-0042",
"deleted": true
}
}
Errores
| HTTP | Código | Cuándo |
|---|---|---|
| 403 | AUTHORIZATION_ERROR | Quien llama no es el dueño de la búsqueda |
| 404 | NOT_FOUND | La búsqueda no existe |
Las que usa y construye otra épica (6)
Su request y su response están en la página de la épica que la construye.
| API | Método y ruta | Para qué, aquí | La construye |
|---|---|---|---|
| API-01-028 | GET /api/v2/taxonomies/PRODUCT/nodes/{id}/effective | Tipar los valores con la vista efectiva de la plantilla | FT-PLT-06 · Configuración de atributos por plantilla y por negocio… |
| API-02-057 | POST /api/v2/product-plans/{planId}/apply | Confirmar el alta, el valor, la bandera o el grupo | FT-FLU-01 · Motor de estados configurable con dos carreteras y reglas… |
| API-02-012 | POST /api/v2/products/{id}/values/plans | Revisar el valor propio de una variante antes de guardarlo | FT-RAC-01 · Motor de validación en línea y candados de datos… |
| API-02-056 | POST /internal/product/bulk-load/products | Cargar lo migrado con las mismas reglas del modelo | FT-ARQ-01 · Núcleo headless |
| API-03-004 | GET /api/v2/migration/quarantine | Ver la cuarentena de productos sin tipo o sin negocio | FT-MIG-03 · Migración inicial de productos desde P360 |
| API-03-008 | POST /internal/migration/identifier-blocks | Tomar bloques de identificadores de la secuencia compartida mientras P360 viva | FT-MIG-04 · Convivencia P360 ↔ nuevo PIM |
6. Eventos
Publica
| Evento | Cuándo | Quién lo consume |
|---|---|---|
catalog.product.changed/PRODUCT_CREATED | Se confirma un alta o un clon | FT-ARQ-01 |
catalog.product.changed/PRODUCT_UPDATED | Cambia un valor, una bandera, la marca «no procede» o la membresía a un grupo | FT-ACT-05, FT-ARQ-02, FT-IA-01, FT-PLT-07, FT-PRD-05, FT-RAC-06 |
catalog.product.changed/VARIANT_LINKED | Una variante se liga a un genérico | FT-ACT-04, FT-PRD-07 |
catalog.product.changed/VARIANT_UNLINKED | Una variante sale de su familia | FT-ACT-04 |
catalog.product.publication/PUBLICATION_REQUESTED | Un aprobado cambia de forma que la tienda debe enterarse: variante que no procede o cambio en un configurable | FT-CAT-05, FT-CAT-06, FT-INT-01, FT-INT-02, FT-INT-05, FT-INT-07, FT-PLT-08, FT-PRD-05, FT-RAC-03 |
Consume
| Evento | Para qué | Quién lo publica |
|---|---|---|
catalog.governance.changed/TEMPLATE_VIEW_CHANGED | MS-02 invalida su caché de vistas efectivas con que tipa y valida los valores | FT-IA-03, FT-MIG-02, FT-PLT-04, FT-PLT-05, FT-PLT-06, FT-PLT-09 |
7. Datos
Cada colección tiene un solo dueño, el único que escribe en ella.
| Colección | Dueño | Qué guarda | Esta épica |
|---|---|---|---|
products | MS-02 | La familia del producto con sus variantes, valores, imágenes (URL), clasificaciones, estado, tareas abiertas y rechazos. | escribe |
identifier_registry | MS-02 | Unicidad de SKU y de EAN por negocio. | escribe |
product_groups | MS-02 | Looks, configurables y venta cruzada. | escribe |
product_sequences | MS-02 | El siguiente bloque de identificadores de producto. | escribe |
product_versions | MS-02 | Foto numerada del producto en cada versión. | escribe |
saved_searches | MS-02 | Búsquedas de productos guardadas por usuario, con su nombre, sus filtros y con qué áreas se comparten. | escribe |
product_receipts | MS-02 | El recibo de cada escritura confirmada en el producto (antes operation_receipts). | escribe |
product_outbox | MS-02 | Los cambios del producto que esperan salir como evento (antes outbox). | escribe |
template_views | MS-01 | La vista efectiva ya resuelta por plantilla y negocio: qué atributos pide y cuáles son obligatorios. | lee |
taxonomy_nodes | MS-01 | Plantillas y nodos de grupo de artículo, con la configuración de atributos de cada plantilla. | lee |
mig_issues | MS-03 | Lo que no se pudo migrar y por qué. | lee |
8. Historias que cubre
| Historia | Prioridad | Cómo la cubre |
|---|---|---|
| FT-PRD-01·H1 · Familia completa | P1 | pasos 2, 4, 5, 8; API-02-002, API-02-003, API-02-014 |
| FT-PRD-01·H2 · Tipo explícito | P1 | pasos 2; flujo «Producto migrado sin tipo»; API-02-005, API-02-056 |
| FT-PRD-01·H3 · Llaves | P1 | pasos 5; API-02-011 |
| FT-PRD-01·H4 · Variante que no procede | P2 | flujo «Una variante que no procede» |
| FT-PRD-01·H5 · Relaciones y banderas | P2 | flujo «Relaciones y banderas con vigencia»; API-02-106, API-02-107 |
| FT-PRD-01·H6 · Herencia del genérico en sus variantes | P1 | pasos 9, 10, 11, 12; API-02-004 |
9. Decisiones de esta épica
Las tomó el TL como arquitecto; quedan como [Propuesta] hasta que el equipo de la épica las revise.
- D-PRD-01-1. Un documento por familia en products con sus variantes adentro; cada variante tiene identificador propio, único en toda la colección. Nada del producto vive en otra base ni en los fronts. Por qué: Un solo dueño del dato: la ficha que ven el portal, Foro, SAP y la tienda es la misma y se lee en una consulta. Se apoya en DEC-PRD-02, ADR-12.
- D-PRD-01-2. El tipo es un campo explícito y obligatorio: GENERIC, GENERIC_MKP, INDIVIDUAL, SET o LOOK; GENERIC_DUMMY y DIGITAL quedan reservados hasta que se firme la tabla. En el alta lo dice quien crea; en la migración se deriva con la regla de P360 y lo que no se resuelve va a cuarentena. Por qué: Hoy el tipo se deduce y unos 110 mil renglones no encajan; decidir atributos, flujo e integraciones necesita un dato, no una deducción. Se apoya en PD-17, DUD-19, RSG-15.
- D-PRD-01-3. Un genérico puede guardarse en Borrador sin variantes; el candado de envío exige al menos una. La regla «genérico sin variantes se rechaza» se aplica al enviar, no al guardar. Por qué: La captura es por pasos y las variantes son un paso propio; rechazar al guardar obligaría a capturar todo de una vez. Se apoya en PD-29, FT-RAC-01.
- D-PRD-01-4. El identificador del producto es texto y nunca cambia; mientras P360 viva sale de bloques de la secuencia compartida (API-03-008). El SKU sólo lo escribe la respuesta de SAP y es inmutable. El EAN es único por negocio en identifier_registry, escrito en la misma transacción que el producto; liberar nunca borra. Por qué: Sin regla de llaves se repiten duplicados (en Omega el SKU no es único) y la convivencia con P360 no puede chocar identificadores. Se apoya en PD-16, DUD-18, DEC-PRD-01, DEC-PRD-04.
- D-PRD-01-5. La herencia se resuelve al leer: la variante muestra los valores comunes del genérico marcados como heredados y sólo guarda lo propio (ejes, modelo, precio y costo). Qué atributo se puede sobrescribir lo dice la configuración de la plantilla en Catálogo. Por qué: Lo común se captura una vez y no se copia a cada variante; un cambio en el genérico llega solo a las hermanas que heredan. Se apoya en DEC-PRD-02, DMS-30.
- D-PRD-01-6. Las relaciones son grupos de primera clase en product_groups (venta cruzada, configurable y look), con un solo grupo de cada tipo por producto. Un cambio en un configurable pide republicar a todos sus miembros. Por qué: Hoy las relaciones son valores sueltos sin integridad; el grupo da ida y vuelta y evita que un producto quede en dos grupos de venta cruzada. Se apoya en DEC-PRD-07, PD-15.
- D-PRD-01-7. Las banderas son una lista explícita con vigencia (inicio y fin). Sin bandera significa ausente, nunca un false de relleno. Por qué: El 99.97 % del arreglo de hoy es relleno en false; guardar sólo lo encendido con fechas da semántica y quita ruido. Se apoya en PD-23, DUD-30.
- D-PRD-01-8. «No procede» es una marca por variante con motivo, que pone QA a mano; la publicación omite esa variante y no frena a sus hermanas. Por qué: La historia pide no frenar a la familia; hacerlo automático sin saber cómo lo decide P360 hoy arriesga sacar variantes buenas. Se apoya en DUD-24.
Qué falta decidir
| ID | Qué falta |
|---|---|
| PD-17 | Firmar la tabla de tipos con Gobierno de Producto y Arquitectura, incluidos el genérico Dummy y los digitales. |
| PD-16 | Ratificar la política de identificadores (prefijo visible o sólo número) con Arquitectura y Gobierno de Producto. |
| PD-23 | Cerrar la lista de banderas y su semántica con Prod E-commerce y Category. |
| DUD-24 | Confirmar cómo marca P360 hoy la variante que no procede, para decidir si además se automatiza. |
| PD-15 | Definir la fuente del Conjunto Look para su grupo. |
Fuentes
- [D]
trazabilidad/features/FT-PRD-01-modelo-canonico-de-producto.md: la ficha de la épica, con sus historias, reglas y bloqueos. - [D] ADR-12 · Un solo sistema: backend compartido y una sola base (sección 3.4).
- [D]
docs/arquitectura/por-epica/base/: piezas, colecciones, eventos y el hilo de punta a punta. - [D]
docs/arquitectura/omega-a-pim-headless/contratos-api.md: convenciones y endpoints de partida. - [D]
docs/arquitectura/maquina-de-estados-to-be/propuesta-maquina-de-estados.md§7: estados, tareas y canales. - [I] Las decisiones de la sección 9 son del TL sobre esas fuentes.