Vehicle compatibility (fitment)

The fitment module answers the question "does this product apply to this vehicle?" — typical for auto parts, where the same SKU can be compatible with multiple combinations of make, model, year, and submodel.

The structure has two levels:

  • Catalog (FitmentCatalog): defines the hierarchy of compatibility levels for your vertical (for example, Make → Model → Year). A catalog can be marked as isDefault.
  • Node (FitmentNode): a concrete value within a catalog level (for example, Make = "Nissan", with a child Model node "Sentra"). Nodes form a tree via parentId/parentLevel.

A product is associated with node combinations via fitmentConfig (within the product payload — see Update a product); this domain exposes the endpoints to manage catalogs/nodes and to query compatibility (search, check).

Shape of `dimensions[]` not verified field by field

The compatibility search and check endpoints (/fitment/search, /fitment/check/{sku}) receive a dimensions[] array that references catalog levels and values, but the audit did not confirm the exact shape of each array element. Confirm it against a real response from GET /products/fitment/catalogs/{id}/nodes (the same level/value returned by that endpoint are what's used to build dimensions[]) before integrating.

Catalogs

Lists compatibility catalogs

typestring

Filters by catalog type.

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" }
    ]
  }
]

Required permission: products:read

Gets a catalog by ID

catalogIdstringrequired

Catalog ID.

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

Required permission: products:read

Creates a compatibility catalog

namestringrequired

Catalog name.

typestringrequired

Catalog type (for example, 'automotive').

levelsarrayrequired

Hierarchy levels, in order. Each `key` must be unique within the catalog and the sequence must be consecutive (no gaps).

isDefaultboolean

Marks this catalog as the tenant's default.

{
  "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" }

Required permission: products:create

Updates a catalog (partial)

catalogIdstringrequired

Catalog ID.

namestring

New name.

levelsarray

Replaces the levels. Must include at least one.

isDefaultboolean

Marks/unmarks as the default catalog.

{ "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" }

Required permission: products:read

RBAC: this write endpoint requires READ, not UPDATE

Unlike the rest of the write operations in this domain, PUT /products/fitment/catalogs/{catalogId} is gated with the products:read permission in the audited source code, without an additional products:update permission. This is a confirmed RBAC inconsistency in the code, not an error in this documentation — report it to support if your integration depends on separating read and write of catalogs by role.

Deletes a catalog

catalogIdstringrequired

Catalog ID.

200
{ "success": true }

Required permission: products:delete

Nodes

Lists the nodes of a catalog level

idstringrequired

Catalog ID.

levelstringrequired

Key of the level to list (for example, 'make').

parentIdstring

Filters by parent node (to navigate the tree, for example all 'model' nodes of a specific 'make').

pagenumber

Page, base 0. Default 0.

limitnumber

Items per page. Default 50.

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

Required permission: products:read

Searches nodes by text within a catalog

idstringrequired

Catalog ID.

qstringrequired

Search term (alias: `query`).

levelstring

Restricts the search to a level.

pagenumber

Page, base 0. Default 0.

limitnumber

Items per page. Default 50.

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

Required permission: products:read

Creates a node within a catalog

idstringrequired

Catalog ID.

levelstringrequired

Key of the level the node belongs to.

valuestringrequired

Normalized value of the node (for example, 'nissan').

labelstring

Readable label (for example, 'Nissan').

parentIdstring

ID of the parent node in the previous level.

parentLevelstring

Key of the parent's level.

rangeobject

Range, used in numeric levels such as year (for example, a range of years).

metadataobject

Free-form metadata associated with the node.

ordernumber

Display order.

{ "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" }

Required permission: products:create

Updates a node (partial)

nodeIdstringrequired

Node ID. Does not require the catalog ID in the route.

valuestring

New value.

labelstring

New label.

rangeobject

New range.

metadataobject

New metadata.

ordernumber

New order.

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

Required permission: products:update

Deletes a node

nodeIdstringrequired

Node ID.

200
{ "success": true }

Required permission: products:delete

Compatibility search and check

Searches products compatible with a combination of dimensions (make/model/year, etc.)

catalogIdstringrequired

Catalog against which compatibility is evaluated.

fitmentTypestring

Restricts to a fitment type, if the catalog supports it.

dimensionsarrayrequired

Level/value combination to search for (see note on unverified shape above).

allowPartialMatchboolean

If `true`, includes products that partially match the dimensions.

matchModestring

Match mode.

{
  "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" }

Required permission: none additional — authentication only (valid Authorization and resolved tenantId). No explicit requirePermission() was confirmed for this endpoint beyond the standard gate by HTTP method.

Checks whether a specific product is compatible with a combination of dimensions

skustringrequired

SKU of the product to check.

catalogIdstringrequired

Catalog against which it's evaluated.

fitmentTypestring

Restricts to a fitment type.

dimensionsarrayrequired

Level/value combination to check.

matchModestring

Match mode.

{
  "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" }

Required permission: same case as /fitment/search — no additional explicit requirePermission(), authentication only.

Response shape partially verified

isCompatible and matchScore in POST /products/fitment/check/{sku} are confirmed; additional fields the response might include (for example, the detail of which dimension didn't match) were not verified field by field.

Import and export a catalog

Bulk imports nodes into a catalog

idstringrequired

Catalog ID.

dataarrayrequired

Array of nodes to import. Cannot be empty.

optionsobject

Import options.

{
  "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" }

Required permission: products:import

Exports all nodes of a catalog

idstringrequired

Catalog ID.

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

Required permission: products:read

Example — search for products compatible with a vehicle

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" }
    ]
  }'

Errors

CodeStatusDescription
bad-request400Missing or invalid required field (catalog name/type/levels, duplicate or non-consecutive levels, inconsistent node hierarchy), a required query parameter is missing (level, q), or data is empty in the import.
not-found404The requested catalog, node, or product doesn't exist.
auth:invalid_token401The API key is invalid or has been revoked.
auth:permission_denied403The API key doesn't have the required permission for this operation.

See the full error catalog for the rest of the possible codes.

Next steps