Compatibilidad de vehículo (fitment)

El módulo de fitment resuelve la pregunta "¿este producto aplica a este vehículo?" — típico de refacciones automotrices, donde un mismo SKU puede ser compatible con múltiples combinaciones de marca, modelo, año y submodelo.

La estructura tiene dos niveles:

  • Catálogo (FitmentCatalog): define la jerarquía de niveles de compatibilidad para tu vertical (por ejemplo, Make → Model → Year). Un catálogo puede marcarse como isDefault.
  • Nodo (FitmentNode): un valor concreto dentro de un nivel del catálogo (por ejemplo, Make = "Nissan", con un Model hijo "Sentra"). Los nodos forman un árbol vía parentId/parentLevel.

Un producto se asocia a combinaciones de nodos mediante fitmentConfig (dentro del payload del producto — ver Actualizar un producto); este dominio expone los endpoints para administrar catálogos/nodos y para consultar compatibilidad (search, check).

Shape de `dimensions[]` no verificado campo a campo

Los endpoints de búsqueda y verificación de compatibilidad (/fitment/search, /fitment/check/{sku}) reciben un arreglo dimensions[] que referencia niveles y valores del catálogo, pero la auditoría no confirmó la forma exacta de cada elemento del arreglo. Confírmala contra una respuesta real de GET /products/fitment/catalogs/{id}/nodes (los mismos level/value que devuelve ese endpoint son los que se usan para construir dimensions[]) antes de integrar.

Catálogos

Lista los catálogos de compatibilidad

typestring

Filtra por tipo de catálogo.

200
[
  {
    "id": "65f4a1b2c4d5e6f7a8b9c0e1",
    "name": "Refacciones automotrices MX",
    "type": "automotive",
    "isDefault": true,
    "levels": [
      { "key": "make", "label": "Marca" },
      { "key": "model", "label": "Modelo" },
      { "key": "year", "label": "Año" }
    ]
  }
]

Permiso requerido: products:read

Obtiene un catálogo por ID

catalogIdstringrequired

ID del catálogo.

404
{ "code": "not-found", "message": "Fitment catalog not found" }

Permiso requerido: products:read

Crea un catálogo de compatibilidad

namestringrequired

Nombre del catálogo.

typestringrequired

Tipo de catálogo (por ejemplo, 'automotive').

levelsarrayrequired

Niveles de la jerarquía, en orden. Cada `key` debe ser único dentro del catálogo y la secuencia debe ser consecutiva (sin saltos).

isDefaultboolean

Marca este catálogo como el predeterminado del tenant.

{
  "name": "Refacciones automotrices MX",
  "type": "automotive",
  "levels": [
    { "key": "make", "label": "Marca" },
    { "key": "model", "label": "Modelo" },
    { "key": "year", "label": "Año" }
  ],
  "isDefault": true
}
400
{ "code": "bad-request", "message": "Level orders must be sequential starting from 0" }

Permiso requerido: products:create

Actualiza un catálogo (parcial)

catalogIdstringrequired

ID del catálogo.

namestring

Nuevo nombre.

levelsarray

Reemplaza los niveles. Debe incluir al menos uno.

isDefaultboolean

Marca/desmarca como catálogo predeterminado.

{ "name": "Refacciones automotrices MX y CA", "isDefault": true }
404
{ "code": "not-found", "message": "Fitment catalog not found" }
400
{ "code": "bad-request", "message": "At least one level is required" }

Permiso requerido: products:read

RBAC: este endpoint de escritura pide READ, no UPDATE

A diferencia del resto de las operaciones de escritura de este dominio, PUT /products/fitment/catalogs/{catalogId} está gateado con el permiso products:read en el código fuente auditado, sin un permiso products:update adicional. Es una inconsistencia de RBAC confirmada en el código, no un error de esta documentación — repórtala a soporte si tu integración depende de separar lectura y escritura de catálogos por rol.

Elimina un catálogo

catalogIdstringrequired

ID del catálogo.

200
{ "success": true }

Permiso requerido: products:delete

Nodos

Lista los nodos de un nivel del catálogo

idstringrequired

ID del catálogo.

levelstringrequired

Clave del nivel a listar (por ejemplo, 'make').

parentIdstring

Filtra por nodo padre (para navegar el árbol, por ejemplo todos los 'model' de un 'make' específico).

pagenumber

Página, base 0. Default 0.

limitnumber

Elementos por página. Default 50.

200
{
  "data": [
    { "id": "65f4b1c2c4d5e6f7a8b9c0f1", "level": "make", "value": "nissan", "label": "Nissan" }
  ],
  "total": 1
}
400
{ "code": "bad-request", "message": "Level parameter is required" }

Permiso requerido: products:read

Busca nodos por texto dentro de un catálogo

idstringrequired

ID del catálogo.

qstringrequired

Término de búsqueda (alias: `query`).

levelstring

Restringe la búsqueda a un nivel.

pagenumber

Página, base 0. Default 0.

limitnumber

Elementos por página. Default 50.

200
{ "data": [{ "id": "65f4b1c2c4d5e6f7a8b9c0f1", "level": "make", "value": "nissan", "label": "Nissan" }], "total": 1 }
400
{ "code": "bad-request", "message": "Search term (q) is required" }

Permiso requerido: products:read

Crea un nodo dentro de un catálogo

idstringrequired

ID del catálogo.

levelstringrequired

Clave del nivel al que pertenece el nodo.

valuestringrequired

Valor normalizado del nodo (por ejemplo, 'nissan').

labelstring

Etiqueta legible (por ejemplo, 'Nissan').

parentIdstring

ID del nodo padre en el nivel anterior.

parentLevelstring

Clave del nivel del padre.

rangeobject

Rango, usado en niveles numéricos como año (por ejemplo, un rango de años).

metadataobject

Metadatos libres asociados al nodo.

ordernumber

Orden de presentación.

{ "level": "model", "value": "sentra", "label": "Sentra", "parentId": "65f4b1c2c4d5e6f7a8b9c0f1", "parentLevel": "make" }
201
{ "id": "65f4b1c2c4d5e6f7a8b9c0f2", "level": "model", "value": "sentra", "label": "Sentra", "parentId": "65f4b1c2c4d5e6f7a8b9c0f1" }
400
{ "code": "bad-request", "message": "Parent level make not found in catalog" }

Permiso requerido: products:create

Actualiza un nodo (parcial)

nodeIdstringrequired

ID del nodo. No requiere el ID del catálogo en la ruta.

valuestring

Nuevo valor.

labelstring

Nueva etiqueta.

rangeobject

Nuevo rango.

metadataobject

Nuevos metadatos.

ordernumber

Nuevo orden.

{ "label": "Nissan (Nissan Mexicana)" }
404
{ "code": "not-found", "message": "Tree node not found" }

Permiso requerido: products:update

Elimina un nodo

nodeIdstringrequired

ID del nodo.

200
{ "success": true }

Permiso requerido: products:delete

Búsqueda y verificación de compatibilidad

Busca productos compatibles con una combinación de dimensiones (marca/modelo/año, etc.)

catalogIdstringrequired

Catálogo contra el que se evalúa la compatibilidad.

fitmentTypestring

Restringe a un tipo de fitment, si el catálogo lo soporta.

dimensionsarrayrequired

Combinación de nivel/valor a buscar (ver nota sobre shape no verificado arriba).

allowPartialMatchboolean

Si es `true`, incluye productos que coinciden parcialmente con las dimensiones.

matchModestring

Modo de coincidencia.

{
  "catalogId": "65f4a1b2c4d5e6f7a8b9c0e1",
  "dimensions": [
    { "level": "make", "value": "nissan" },
    { "level": "model", "value": "sentra" },
    { "level": "year", "value": "2022" }
  ]
}
200
[
  { "sku": "REF-PASTILLA-FRENO-01", "title": "Juego de pastillas de freno delanteras", "matchScore": 1 }
]
400
{ "code": "bad-request", "message": "Catalog ID is required" }

Permiso requerido: ninguno adicional — solo autenticación (Authorization válido y tenantId resuelto). No se confirmó un requirePermission() explícito para este endpoint más allá del gate estándar por método HTTP.

Verifica si un producto específico es compatible con una combinación de dimensiones

skustringrequired

SKU del producto a verificar.

catalogIdstringrequired

Catálogo contra el que se evalúa.

fitmentTypestring

Restringe a un tipo de fitment.

dimensionsarrayrequired

Combinación de nivel/valor a verificar.

matchModestring

Modo de coincidencia.

{
  "catalogId": "65f4a1b2c4d5e6f7a8b9c0e1",
  "dimensions": [
    { "level": "make", "value": "nissan" },
    { "level": "model", "value": "sentra" },
    { "level": "year", "value": "2022" }
  ]
}
200
{ "isCompatible": true, "matchScore": 1 }
404
{ "code": "not-found", "message": "Product REF-PASTILLA-FRENO-01 not found" }

Permiso requerido: mismo caso que /fitment/search — sin requirePermission() explícito adicional, solo autenticación.

Shape de respuesta parcialmente verificado

isCompatible y matchScore en POST /products/fitment/check/{sku} están confirmados; campos adicionales que la respuesta pudiera incluir (por ejemplo, el detalle de qué dimensión no coincidió) no fueron verificados campo a campo.

Importa nodos en bloque a un catálogo

idstringrequired

ID del catálogo.

dataarrayrequired

Arreglo de nodos a importar. No puede estar vacío.

optionsobject

Opciones de importación.

{
  "data": [
    { "level": "make", "value": "nissan", "label": "Nissan" },
    { "level": "model", "value": "sentra", "label": "Sentra", "parentId": "nissan" }
  ]
}
200
{ "totalProcessed": 150, "inserted": 120, "updated": 25, "skipped": 5, "errors": [] }
400
{ "code": "bad-request", "message": "Import data is required" }

Permiso requerido: products:import

Exporta todos los nodos de un catálogo

idstringrequired

ID del catálogo.

200
{ "data": [{ "id": "65f4b1c2c4d5e6f7a8b9c0f1", "level": "make", "value": "nissan", "label": "Nissan" }], "count": 1 }
404
{ "code": "not-found", "message": "Catalog 65f4a1b2c4d5e6f7a8b9c0e1 not found" }

Permiso requerido: products:read

Ejemplo — buscar productos compatibles con un vehículo

curl -X POST https://api.fenicia.io/products/fitment/search \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "catalogId": "65f4a1b2c4d5e6f7a8b9c0e1",
    "dimensions": [
      { "level": "make", "value": "nissan" },
      { "level": "model", "value": "sentra" },
      { "level": "year", "value": "2022" }
    ]
  }'

Errores

CódigoStatusDescripción
bad-request400Campo requerido faltante o inválido (nombre/tipo/niveles del catálogo, niveles duplicados o no consecutivos, jerarquía de nodo inconsistente), falta un parámetro de consulta requerido (level, q), o data viene vacío en la importación.
not-found404No existe el catálogo, nodo o producto solicitado.
auth:invalid_token401La API key es inválida o fue revocada.
auth:permission_denied403La API key no tiene el permiso requerido para esta operación.

Consulta el catálogo completo de errores para el resto de los códigos posibles.

Siguientes pasos