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.

    Field mapping

    View source

    Source data carries its own names and shapes: name instead of @title, price_with_vat as a string instead of price as a number, availability as "in stock" instead of 1.

    A mapping is a small declarative document that says which source field becomes which catalog field, and what to do to the value on the way. Nothing about your export or your payloads has to change.

    Mapping sits in front of both ways in: content arriving from a feed and content pushed over the Content API are mapped the same way, by object type.

    Mappings are usually written with your Luigi’s Box implementation contact during onboarding. This page is for reading the one you have, and for changing it yourself over the feed management API.

    A mapping is a JSON object of source → target:

    {
    "name": "@title",
    "product_id": "@id",
    "price_with_vat": "price | to_f",
    "stock_status": "availability | translate('in stock', 1, 'out of stock', 0) | to_i"
    }
    • The key selects a field on the incoming record. Dot-paths reach into nested structures; * matches a group of names.
    • The value is a target spec: an optional target field name, then any number of |-chained transforms.

    Fields you do not mention pass through unchanged, so a mapping only needs to describe what differs.

    The target spec is the new name:

    {
    "name": "@title",
    "product_id": "@id",
    "link": "url",
    "img": "image_url"
    }

    Chain transforms after a |. They run left to right.

    {
    "price_with_vat": "price | to_f",
    "description": "description | decode_html_entities",
    "link": "url | strip_all_parameters"
    }

    To transform a field in place, without renaming it, omit the target name and start with the pipe:

    {
    "price": "| to_f",
    "title": "| decode_html_entities"
    }

    Applied to a list, a transform runs on each element.

    Type conversion. A field’s first value fixes its type for the whole catalog, and a price stored as text cannot be range-filtered.

    TransformEffect
    to_fTo a number. 0.0 if the value is missing or unparseable.
    to_iTo a whole number, truncating decimals. 0 on failure.
    to_sTo text. Empty string for a missing value.
    to_boolTo true/false. Accepts 1/0, y/n, yes/no, t/f, true/false.
    to_dateTo an ISO date, YYYY-MM-DD.
    to_timeTo an ISO 8601 timestamp.
    json_parseParse a JSON string into real structure.

    Text.

    TransformEffect
    replace('find', 'with')Replace every occurrence
    prefix('text') / append('text')Add text at the start or end
    capitalizeFirst character upper, rest lower
    split_by_separator('/')Split into a list, trimming each part
    decode_html_entitiesDecode entities and strip HTML tags
    strip_all_parametersDrop the query string from a URL
    translate('a', 1, 'b', 2)Map values through a lookup table; unmatched values pass through

    Lists.

    TransformEffect
    to_arrayWrap a single value in a list
    first / lastKeep only the first or last element
    drop_lastDrop the last element
    flattenFlatten one level of nesting
    uniqRemove duplicates, keeping order
    unpack_arrayUnwrap a one-element list to the value itself
    valuesTake a nested object’s values as a list

    Numbers.

    TransformEffect
    min / maxThe smallest or largest of the value, optionally against a second value

    prefix is how you build typed identities from a bare key:

    { "product_id": "@id | prefix('product/')" }

    A source product_id of sku-1001 becomes @id: "product/sku-1001" — the form identity requires.

    A transform argument beginning with @ reads another field from the same record:

    { "sale_price": "price | min(@list_price)" }

    If the referenced field is absent, the transform is skipped rather than failing the record.

    Give a list of target specs to write one source field to several places — including back to its own name, which is how you keep a field while also deriving from it:

    {
    "name": ["@title", "name"],
    "category_path": ["@category | split_by_separator(' > ') | last", "category_path"]
    }

    Dot-paths reach into structure:

    {
    "attributes.color": "color",
    "shipping.price": "shipping_price | to_f"
    }

    A . as the target name spreads an object’s keys into the record itself, which flattens a nested block:

    { "params": "." }

    Applied to a record:

    // before
    { "params": { "material": "cotton", "fit": "regular" } }
    // after
    { "material": "cotton", "fit": "regular" }

    * matches a group of source names, and \1 in the target inserts what it matched:

    { "custom_*": "\\1" }

    custom_color becomes color, custom_fit becomes fit. Useful for feeds that prefix every extra attribute.

    An @defaults block fills in fields the record does not have. It runs before the rest of the mapping, and it never overwrites a value that is already present:

    {
    "@defaults": {
    "availability": 1,
    "currency": "EUR"
    },
    "price_with_vat": "price | to_f"
    }

    Values here can reference other fields with @, and a reference to an absent field is skipped.

    POST /catalog/v1/{catalog_id}/mappings/validate applies a mapping to a sample record and returns the result. Nothing is stored.

    Terminal window
    curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/mappings/validate' \
    -H 'Authorization: Bearer <token>' \
    -H 'Content-Type: application/json' \
    -d '{
    "mapping": {
    "name": "@title",
    "product_id": "@id | prefix(\"product/\")",
    "price_with_vat": "price | to_f",
    "stock_status": "availability | translate(\"in stock\", 1, \"out of stock\", 0) | to_i"
    },
    "sample": {
    "product_id": "sku-1001",
    "name": "Blue Cotton T-Shirt",
    "price_with_vat": "29.90",
    "stock_status": "in stock"
    }
    }'

    The response is the mapped record:

    {
    "result": {
    "@id": "product/sku-1001",
    "@title": "Blue Cotton T-Shirt",
    "price": 29.9,
    "availability": 1
    }
    }

    Use one record copied verbatim from your real feed as the sample. A hand-written example can differ from the export in exactly the detail the mapping has to handle.

    OperationEndpoint
    ListGET /catalog/v1/{catalog_id}/mappings
    CreatePOST /catalog/v1/{catalog_id}/mappings
    ReadGET /catalog/v1/{catalog_id}/mappings/{mapping_id}
    UpdatePATCH /catalog/v1/{catalog_id}/mappings/{mapping_id}
    DeleteDELETE /catalog/v1/{catalog_id}/mappings/{mapping_id}

    A mapping belongs to a catalog and an object type (catalog_type), and a feed points at one by mapping_id. See Feed management API.

    Changing a mapping affects records processed after the change. To re-apply it to your whole catalog, reset the feed’s fingerprints and resynchronize — see Forcing a full resend.