Skip to content

    Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.

    Facets

    View source

    Facets are the filter sidebar: the brands present in these results, the price range they span, the category tree they sit in. POST /discovery/v1/facets computes them for a result set you already fetched, rather than re-running the query.

    The request takes a guid rather than a query, so facets and results always describe the same result set.

    Terminal window
    curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/facets?channel_id=lbn_4hj9tv' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -d '{
    "guid": "3ab549eb64a20a0c",
    "facets": [
    { "field": "brand", "size": 20 },
    { "field": "color" },
    { "field": "price" }
    ]
    }'
    Query parameterRequiredNotes
    channel_idYesServing destination
    Body fieldRequiredNotes
    guidYesFrom a previous /search or /collections response
    facetsYes1–100 entries of { "field": …, "size": … }
    hierarchy_anchorsNoWhich branches of a hierarchy to expand. Up to 50.
    hierarchy_depthsNoHow many child levels to expand, per field. Up to 50.

    There is no surface_id: the guid already identifies the result set, and with it the surface that produced it. A token authorized for either search or collections may call this endpoint.

    size limits how many values a field returns:

    { "field": "brand", "size": 20 }

    Omit it for the surface’s default. Cap long-tail fields such as brand.

    Facets come back keyed by field name, each tagged with its type:

    {
    "guid": "3ab549eb64a20a0c",
    "facets": {
    "brand": {
    "type": "terms",
    "field": "brand",
    "values": ["northwear", "atlas", "kestrel"]
    },
    "price": {
    "type": "range",
    "field": "price",
    "min": 12.5,
    "max": 249.0
    },
    "lbx:category_hierarchy": {
    "type": "hierarchy",
    "field": "lbx:category_hierarchy",
    "values": [
    {
    "value": "category/clothing",
    "title": "Clothing",
    "has_children": true,
    "children": []
    }
    ]
    }
    }
    }

    Switch on type to pick a control:

    typeFieldsRender as
    termsvalues — the distinct values presentCheckboxes or a multi-select
    rangemin, max — the bounds in these resultsA slider or two number inputs
    hierarchyvalues — a tree of nodesAn expandable tree
    function renderFacet(name, facet) {
    switch (facet.type) {
    case 'terms': return checkboxes(name, facet.values);
    case 'range': return slider(name, facet.min, facet.max);
    case 'hierarchy': return tree(name, facet.values);
    }
    }

    Which fields a surface can facet hierarchically is part of its configuration — ask your Luigi’s Box contact to set one up.

    A hierarchy node’s value is its fully expanded path, with segments joined by >:

    category/clothing > category/shirts

    title is the node’s own display name, and has_children tells you whether it can be expanded. By default only the roots come back, with children empty.

    Two fields open the tree. hierarchy_depths expands from the roots downwards:

    {
    "hierarchy_depths": [
    { "field": "lbx:category_hierarchy", "depth": 2 }
    ]
    }

    depth must be between 1 and 5, and each field may appear at most once. Anything else is a 422.

    hierarchy_anchors opens one specific branch, naming the full node path:

    {
    "hierarchy_anchors": [
    { "field": "lbx:category_hierarchy", "path": "category/clothing > category/shirts" }
    ]
    }

    List several entries to open several branches, across several fields. The path is a JSON string, so the > separator needs no encoding.

    const body = {
    guid,
    facets: [{ field: 'lbx:category_hierarchy' }],
    hierarchy_anchors: openBranches.map((path) => ({ field: 'lbx:category_hierarchy', path })),
    };

    Hold the set of open branches in your UI state and send it whole on each request, so the tree renders in the state the shopper left it in.

    Facets and results move together. Each new filter combination is a new result set:

    1. POST /searchhits, guid.
    2. POST /facets with that guid → the sidebar.
    3. Shopper checks northwearPOST /search again with "filters": { "brand": { "in": ["northwear"] } } → new hits, new guid.
    4. POST /facets with the new guid → the sidebar, now narrowed to what is still reachable.

    Never reuse a guid after changing filters. The facets you get back would describe the previous result set, and the sidebar would offer values that no longer match anything.

    Calling /facets also computes an exact count, so a total that came back null from the search becomes a real number.

    • Facet on what a shopper filters by, not on everything you index. Brand, price, colour, size, availability.
    • Cap the long tail with size, for example { "field": "brand", "size": 20 }.
    • Numeric fields need to be numeric. A price indexed as text facets as a terms list of strings, not a range. Check with catalog metadata and fix it in the mapping.