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.
Feeds are normally configured with your Luigi’s Box implementation contact. When you want to manage them yourself — a new market, a URL rotation, a scripted resynchronization — the catalog configuration API does the same job.
Everything here needs a token for the https://api.<region>.luigisbox.ai/catalog audience,
and a grant covering the operation — feeds, mappings, syncs and metadata are granted
separately, and reading is separate from writing. See
Server-to-server tokens.
The pieces
Section titled “The pieces”| Resource | What it is |
|---|---|
| Feed | One URL, one object type, its format and its parsing options |
| Mapping | How source fields become catalog fields — see Field mapping. Shared with the Content API. |
| Synchronization | One run: download, parse, send. Recorded whether it succeeds or not. |
One catalog holds several feeds — products, categories, brands, articles — each pointing at a mapping.
Registering a feed
Section titled “Registering a feed”curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com/feeds/products.xml.gz", "catalog_type": "product", "format": "xml", "item_tag": "product", "identity_field": "product_id", "mapping_id": 17, "active": true }'The response is the created feed, including its feed_id and version.
| Field | Required | Purpose |
|---|---|---|
url | Yes | Where to fetch the feed. HTTPS, no interactive login. |
catalog_type | Yes | The object type in this file: product, category, brand, article |
format | No | xml, json, json_lines, csv. Detected when omitted. |
item_tag | No | XML element that wraps one record, e.g. product |
json_path | No | Path to the record array in a JSON feed, e.g. products |
csv_delimiter / csv_has_header | No | CSV parsing options |
identity_field | No | Source field holding the record’s identity |
mapping_id | No | The mapping to apply |
group_id_field / group_primary_field | No | Source fields for variant grouping |
delete_threshold | No | Fraction of the catalog a run may remove, 0.0–1.0 |
active | No | Defaults to true. false registers without scheduling. |
position | No | Ordering among the catalog’s feeds |
transport_options | No | Extra options for fetching, such as authentication headers |
Format, item tag and JSON path are detected from the file when you leave them out. Set them explicitly for a production feed: detection is a convenience, not a contract.
Reading and changing a feed
Section titled “Reading and changing a feed”# One feedcurl 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41' \ -H 'Authorization: Bearer <token>'
# All of a catalog's feedscurl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds' \ --data-urlencode 'page=1' --data-urlencode 'size=20' \ -H 'Authorization: Bearer <token>'Updates are PATCH, and they carry the version you last read as expected_version:
curl -X PATCH 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com/feeds/products-v2.xml.gz", "expected_version": 7 }'If the feed changed since you read it, the version no longer matches and the update is rejected instead of overwriting the other change. Re-read the feed and reapply your change.
Omitted fields are left unchanged; a field sent as null is cleared. See
Partial updates.
DELETE deactivates a feed. It stops being fetched; the catalog content it produced stays
where it is. To remove the content too, delete the objects — see
Content API.
Triggering a synchronization
Section titled “Triggering a synchronization”Feeds are checked automatically. Trigger one by hand when you have just changed a mapping, or want to pull a fresh export immediately:
curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sync' \ -H 'Authorization: Bearer <token>'The response identifies the run:
{ "run_id": "9f2c4b71-3e8a-4d05-b6c1-7a3d9e2f8041" }This covers the whole catalog — every active feed on it.
Forcing a full resend
Section titled “Forcing a full resend”Between runs, Luigi’s Box remembers which records it has already seen, so an unchanged record is not re-sent. A mapping change therefore does not reshape records that did not change.
Reset the memory to force everything through again:
curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/fingerprints/reset' \ -H 'Authorization: Bearer <token>'Then synchronize. The next run re-sends every record in the feed.
Do this after changing a mapping, after adding a field you want backfilled, or when a catalog and a feed have drifted apart. Do not do it routinely — it is a full reindex of that feed’s content.
The fingerprints=false query parameter on /sync has the same effect for one run without
clearing the stored state.
Checking status
Section titled “Checking status”curl 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/status' \ -H 'Authorization: Bearer <token>'The latest run, plus what was detected and remembered about the feed:
{ "feed_id": 41, "locked": false, "latest_run": { "id": 5182, "status": "succeeded", "trigger": "schedule", "started_at": "2026-03-14T02:00:04Z", "finished_at": "2026-03-14T02:04:31Z", "items_sent": 207993, "items_skipped": 0, "items_no_identity": 0, "error_message": null }, "state": { "detected_format": "xml", "detected_item_tag": "product", "total_items": 207993, "last_run_at": "2026-03-14T02:00:04Z" }}locked: true means a run is in progress. state reports what was detected and remembered
about the feed; use it to confirm that format detection agrees with your configuration.
Run history
Section titled “Run history”curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/syncs' \ --data-urlencode 'size=20' \ -H 'Authorization: Bearer <token>'status | Meaning |
|---|---|
running | In progress |
succeeded | Completed |
failed | Stopped — read error_message and status_reason |
skipped | Nothing to do; the feed had not changed since the last run |
A long stretch of skipped runs means your ETag or Last-Modified headers work and the
unchanged file is not downloaded. A long stretch of skipped when the file has changed
means those headers are wrong — see
Serving your feed.
The counters on each run:
| Counter | Watch for |
|---|---|
items_sent | A sudden drop — an incomplete export |
items_skipped | Anything non-zero — records rejected during parsing |
items_no_identity | Anything non-zero — records with no value in identity_field |
Managing mappings
Section titled “Managing mappings”Mappings live under the same catalog:
GET /catalog/v1/{catalog_id}/mappingsPOST /catalog/v1/{catalog_id}/mappingsPOST /catalog/v1/{catalog_id}/mappings/validateGET /catalog/v1/{catalog_id}/mappings/{mapping_id}PATCH /catalog/v1/{catalog_id}/mappings/{mapping_id}DELETE /catalog/v1/{catalog_id}/mappings/{mapping_id}Always dry-run a change through /validate before saving it. See
Field mapping.
See also
Section titled “See also”- Feeds overview — hosting requirements and formats
- Field mapping · Catalog metadata
- Object history — tracing one object through a run
- Feed troubleshooting
Was this page helpful?
Thanks.