> For the complete documentation index, see [llms.txt](https://docs.limio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limio.com/api/catalog-api/catalog-management.md).

# Catalog Management

Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.

Use them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.

To find records by type, tag or attribute, use Catalog instead.

## Bulk Update

> Changes many catalog items in one request: offers, add-ons and the other record types. Use it to change the same attribute on many items at once, for example the price of many offers.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"schemas":{"BulkUpdateCatalogItemResponse":{"type":"object","properties":{"success":{"type":"boolean"},"failed":{"type":"array","items":{"type":"string"}}}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/bulk-update":{"put":{"tags":["Catalog Management"],"operationId":"bulkUpdate","summary":"Bulk Update","description":"Changes many catalog items in one request: offers, add-ons and the other record types. Use it to change the same attribute on many items at once, for example the price of many offers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"diffs":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The offer path in Limio"},"diff":{"type":"object","properties":{"attributes":{"type":"object","description":"The attributes on the offer that will be updated","properties":{"example_attribute":{"type":"string"}}}}}}}}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateCatalogItemResponse"}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"},"404":{"description":"Not found"}}}}}}
```

## Get Catalog Item

> Returns the item at \`path\`, and the items below it. An item is an offer, a product, an add-on, a page, a segment or a discount; \`record\_type\` says which. The response comes from the catalog working tree, so it gives you the record exactly as it is stored.\
> \
> Use this endpoint when you know the path and you need one specific item: its attribute values, its base template, or its price rows. Because it reads the working tree, it returns an item as soon as you create it, before a publish.\
> \
> Do not use this endpoint to search. To find items by record type, tag or attribute, use \`GET /api/catalog\_items\`, which queries the search index and is built for large result sets.\
> \
> This endpoint is immediately consistent. The index-backed endpoints can lag a write by some seconds, so read an item back here, not from the index.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}},"catalogItemWithDetails":{"in":"query","name":"withDetails","description":"Set to `true` to include the data of each item. The default response gives the metadata of the item only.","schema":{"type":"boolean"}},"catalogItemRecursive":{"in":"query","name":"recursive","description":"Set to `true` to include every item below the path. The default response includes the direct children only.","schema":{"type":"boolean"}},"catalogItemMetadataOnly":{"in":"query","name":"metadataOnly","description":"Set to `true` to leave the attributes out and return the identity of each item only: its path, name, type and record type. Use it to list what a tree holds when the items themselves are large.","schema":{"type":"boolean"}}},"schemas":{"CatalogItem":{"type":"object","description":"An item, in the shape that a read returns.","properties":{"path":{"type":"string","description":"The full catalog path of the item, with a leading slash."},"name":{"type":"string"},"type":{"type":"string","description":"`item` for an item, `dir` for a tree node that holds other items.","enum":["item","dir"]},"id":{"type":"string","description":"A hash of the content of the item. The platform computes it on every read, so it changes when the item changes. It is not settable."},"version":{"type":"string","description":"The content version. The platform derives it. It is not settable."},"data":{"$ref":"#/components/schemas/CatalogItemInput"},"tree":{"type":"array","description":"The items below this path. It is empty when the item has no children.","items":{"type":"object"}}}},"CatalogItemInput":{"type":"object","description":"The content of the item. The platform stores it as you send it, after it validates the attributes.\n\n`id` and `version` are not settable. The platform derives both on every read, and it ignores values that you send under those names.","properties":{"name":{"type":"string","description":"The display name. Make it the same as the last segment of the path."},"record_type":{"type":"string","description":"The record type of the item.","enum":["add_on","discount","family","offer","page","product","segment","template"]},"baseTemplate":{"type":"string","description":"The path of the template that the item is built from. It is required when the body has `attributes`."},"attributes":{"type":"object","additionalProperties":true,"description":"The attribute values, keyed by the full attribute name including the `__limio` suffix. The platform checks each one against the type that the family gives it: a picklist against its options, and a string, number, boolean, array or object against that type. It does not check the shape inside an object, so a wrong shape there is stored as sent. It fills in the attributes that the family gives a default and you leave out.\n\nRead the template family first to learn which attributes exist and which values they accept. `GET /limio/catalogs/1/items/config/templates/offers` returns the offers family; the families are `offers`, `products`, `add_ons`, `pages`, `segments` and `discounts`.","properties":{"price__limio":{"type":"array","description":"The charges of an offer. The family does not give the shape of a charge, so read `OfferPrice` for it.","items":{"$ref":"#/components/schemas/OfferPrice"}}}},"products":{"type":"array","description":"The catalog paths of the products that an offer sells, for example `/products/Digital`. Send paths only: a read answers with each product expanded, but a write takes the path. An offer with no product sells nothing.","items":{"type":"string"}},"productBundles":{"type":"array","description":"The rate plan of each product in `products`, and its share of the price. Send one entry per product.","items":{"type":"object","properties":{"product_path":{"type":"string"},"rate_plan":{"type":"string"},"revenue_split":{"type":"string","description":"The share of the price, in percent."}}}}},"required":["record_type"]},"OfferPrice":{"type":"object","description":"One charge of an offer. `price__limio` holds an array of charges, and a subscription to the offer pays every charge in the array. The family types `price__limio` only as `price`, and the platform does not check the shape inside it. A wrong shape is stored as sent.\n\n`type` selects the charge. `recurring` bills `value` on each interval. `onetime` bills `value` one time. `recurringVolume` bills by quantity, from `tiers`. `usagePerUnit` bills `value` for each unit that the customer uses.\n\nA trial is two charges. The first charge bills the trial price with `repeat_count` set to the trial length. The second charge bills the full price, with `delay_interval` and `delay_interval_type` set to the same length, so that it starts when the trial ends.","properties":{"name":{"type":"string","description":"The identifier of the charge, unique in the array. The admin app writes `charge_1`, `charge_2` and so on."},"label":{"type":"string"},"type":{"type":"string","enum":["recurring","onetime","recurringVolume","usagePerUnit"]},"currencyCode":{"type":"string","description":"The ISO 4217 code of the currency."},"value":{"type":"string","description":"The amount, as a string. Every type has it except `recurringVolume`, which uses `tiers`."},"trigger":{"type":"string","description":"When the charge starts. `subscription_start` is the start date of the subscription. `order_date` is the date of the order. `delay` starts the charge after `delay_interval`.","enum":["subscription_start","order_date","delay"]},"delay_interval":{"type":"integer","description":"The length of the delay before the first bill of this charge."},"delay_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_interval":{"type":"integer","description":"The billing period of a recurring charge, or the term length of a termed `onetime` charge."},"repeat_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_count":{"type":"integer","description":"The number of periods that the charge bills. Leave it out, or send 0, to bill until the subscription ends."},"term_setting":{"type":"string","description":"Only for `onetime`. `termed` gives access for `repeat_count` terms of `repeat_interval`. `evergreen` has no end date. `line_item` is a purchase that gives no access period.","enum":["termed","evergreen","line_item"]},"tiers":{"type":"array","description":"Only for `recurringVolume`. The tiers must cover the units from 1 without a gap. Leave out `ending_unit` on the last tier.","items":{"type":"object","properties":{"starting_unit":{"type":"integer"},"ending_unit":{"type":"integer"},"price":{"type":"string"},"price_format":{"type":"string","description":"`PerUnit` bills `price` for each unit. `FlatFee` bills `price` one time for the tier.","enum":["PerUnit","FlatFee"]}},"required":["starting_unit","price","price_format"]}},"use_external_price":{"type":"boolean","description":"When true, the billing system prices the charge, and `value` is shown only."}},"required":["name","type","currencyCode"]},"CatalogError":{"type":"object","description":"The error contract of the catalog management endpoints. `error.code` names the reason, so automation can branch on it without reading the message.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"`NO_TEMPLATE` — the body has attributes but no `baseTemplate`, or the `baseTemplate` path does not exist. `INVALID_ATTRIBUTE_SCHEMA` — an attribute value does not agree with its definition in the template family. The message names the attribute and the rule that failed. `NO_RECORD_TYPE` — the path and the body together do not settle a record type. Send `record_type`. `EXISTING` — an item is already at this path. `NOTFOUND` — there is no item at this path."},"message":{"type":"string","description":"The reason in plain text."}}}}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/items/{path}":{"get":{"tags":["Catalog Management"],"operationId":"getCatalogItem","summary":"Get Catalog Item","description":"Returns the item at `path`, and the items below it. An item is an offer, a product, an add-on, a page, a segment or a discount; `record_type` says which. The response comes from the catalog working tree, so it gives you the record exactly as it is stored.\n\nUse this endpoint when you know the path and you need one specific item: its attribute values, its base template, or its price rows. Because it reads the working tree, it returns an item as soon as you create it, before a publish.\n\nDo not use this endpoint to search. To find items by record type, tag or attribute, use `GET /api/catalog_items`, which queries the search index and is built for large result sets.\n\nThis endpoint is immediately consistent. The index-backed endpoints can lag a write by some seconds, so read an item back here, not from the index.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"},{"$ref":"#/components/parameters/catalogItemWithDetails"},{"$ref":"#/components/parameters/catalogItemRecursive"},{"$ref":"#/components/parameters/catalogItemMetadataOnly"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"header":{"type":"object","description":"The commit that the read came from.","properties":{"commitId":{"type":"string"}}},"item":{"$ref":"#/components/schemas/CatalogItem"}}}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"},"404":{"description":"There is no item at this path. `error.code` is `NOTFOUND`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogError"}}}}}}}}}
```

## Create Catalog Item

> Creates a new item at \`path\`: an offer, a product, an add-on, a page, a segment or a discount. \`record\_type\` says which, and \`baseTemplate\` names the template it follows. The platform validates the attributes against the family of the base template, writes the item, queues a catalog commit, and adds the item to the search index before it answers.\
> \
> The template family of a record type holds the attributes it accepts. \`GET /limio/catalogs/1/items/config/templates/offers\` returns the offers family: \`attributeDefinitions\` gives each attribute with its type and its allowed values, and \`tree\` lists the templates you can name as \`baseTemplate\`. The families are \`offers\`, \`products\`, \`add\_ons\`, \`pages\`, \`segments\` and \`discounts\`. Read the family itself, without \`recursive\`: a template below it only names the attributes it uses.\
> \
> Use \`POST /limio/jobs\` instead for a bulk load of hundreds of items. Every create on this endpoint starts a catalog commit.\
> \
> The platform writes the item before it indexes it. If the answer is a 5xx, read the item back before you retry: the write can have landed, and a retry then answers \`422\`.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}}},"schemas":{"CatalogItemInput":{"type":"object","description":"The content of the item. The platform stores it as you send it, after it validates the attributes.\n\n`id` and `version` are not settable. The platform derives both on every read, and it ignores values that you send under those names.","properties":{"name":{"type":"string","description":"The display name. Make it the same as the last segment of the path."},"record_type":{"type":"string","description":"The record type of the item.","enum":["add_on","discount","family","offer","page","product","segment","template"]},"baseTemplate":{"type":"string","description":"The path of the template that the item is built from. It is required when the body has `attributes`."},"attributes":{"type":"object","additionalProperties":true,"description":"The attribute values, keyed by the full attribute name including the `__limio` suffix. The platform checks each one against the type that the family gives it: a picklist against its options, and a string, number, boolean, array or object against that type. It does not check the shape inside an object, so a wrong shape there is stored as sent. It fills in the attributes that the family gives a default and you leave out.\n\nRead the template family first to learn which attributes exist and which values they accept. `GET /limio/catalogs/1/items/config/templates/offers` returns the offers family; the families are `offers`, `products`, `add_ons`, `pages`, `segments` and `discounts`.","properties":{"price__limio":{"type":"array","description":"The charges of an offer. The family does not give the shape of a charge, so read `OfferPrice` for it.","items":{"$ref":"#/components/schemas/OfferPrice"}}}},"products":{"type":"array","description":"The catalog paths of the products that an offer sells, for example `/products/Digital`. Send paths only: a read answers with each product expanded, but a write takes the path. An offer with no product sells nothing.","items":{"type":"string"}},"productBundles":{"type":"array","description":"The rate plan of each product in `products`, and its share of the price. Send one entry per product.","items":{"type":"object","properties":{"product_path":{"type":"string"},"rate_plan":{"type":"string"},"revenue_split":{"type":"string","description":"The share of the price, in percent."}}}}},"required":["record_type"]},"OfferPrice":{"type":"object","description":"One charge of an offer. `price__limio` holds an array of charges, and a subscription to the offer pays every charge in the array. The family types `price__limio` only as `price`, and the platform does not check the shape inside it. A wrong shape is stored as sent.\n\n`type` selects the charge. `recurring` bills `value` on each interval. `onetime` bills `value` one time. `recurringVolume` bills by quantity, from `tiers`. `usagePerUnit` bills `value` for each unit that the customer uses.\n\nA trial is two charges. The first charge bills the trial price with `repeat_count` set to the trial length. The second charge bills the full price, with `delay_interval` and `delay_interval_type` set to the same length, so that it starts when the trial ends.","properties":{"name":{"type":"string","description":"The identifier of the charge, unique in the array. The admin app writes `charge_1`, `charge_2` and so on."},"label":{"type":"string"},"type":{"type":"string","enum":["recurring","onetime","recurringVolume","usagePerUnit"]},"currencyCode":{"type":"string","description":"The ISO 4217 code of the currency."},"value":{"type":"string","description":"The amount, as a string. Every type has it except `recurringVolume`, which uses `tiers`."},"trigger":{"type":"string","description":"When the charge starts. `subscription_start` is the start date of the subscription. `order_date` is the date of the order. `delay` starts the charge after `delay_interval`.","enum":["subscription_start","order_date","delay"]},"delay_interval":{"type":"integer","description":"The length of the delay before the first bill of this charge."},"delay_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_interval":{"type":"integer","description":"The billing period of a recurring charge, or the term length of a termed `onetime` charge."},"repeat_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_count":{"type":"integer","description":"The number of periods that the charge bills. Leave it out, or send 0, to bill until the subscription ends."},"term_setting":{"type":"string","description":"Only for `onetime`. `termed` gives access for `repeat_count` terms of `repeat_interval`. `evergreen` has no end date. `line_item` is a purchase that gives no access period.","enum":["termed","evergreen","line_item"]},"tiers":{"type":"array","description":"Only for `recurringVolume`. The tiers must cover the units from 1 without a gap. Leave out `ending_unit` on the last tier.","items":{"type":"object","properties":{"starting_unit":{"type":"integer"},"ending_unit":{"type":"integer"},"price":{"type":"string"},"price_format":{"type":"string","description":"`PerUnit` bills `price` for each unit. `FlatFee` bills `price` one time for the tier.","enum":["PerUnit","FlatFee"]}},"required":["starting_unit","price","price_format"]}},"use_external_price":{"type":"boolean","description":"When true, the billing system prices the charge, and `value` is shown only."}},"required":["name","type","currencyCode"]},"CatalogError":{"type":"object","description":"The error contract of the catalog management endpoints. `error.code` names the reason, so automation can branch on it without reading the message.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"`NO_TEMPLATE` — the body has attributes but no `baseTemplate`, or the `baseTemplate` path does not exist. `INVALID_ATTRIBUTE_SCHEMA` — an attribute value does not agree with its definition in the template family. The message names the attribute and the rule that failed. `NO_RECORD_TYPE` — the path and the body together do not settle a record type. Send `record_type`. `EXISTING` — an item is already at this path. `NOTFOUND` — there is no item at this path."},"message":{"type":"string","description":"The reason in plain text."}}}}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/items/{path}":{"post":{"tags":["Catalog Management"],"operationId":"createCatalogItem","summary":"Create Catalog Item","description":"Creates a new item at `path`: an offer, a product, an add-on, a page, a segment or a discount. `record_type` says which, and `baseTemplate` names the template it follows. The platform validates the attributes against the family of the base template, writes the item, queues a catalog commit, and adds the item to the search index before it answers.\n\nThe template family of a record type holds the attributes it accepts. `GET /limio/catalogs/1/items/config/templates/offers` returns the offers family: `attributeDefinitions` gives each attribute with its type and its allowed values, and `tree` lists the templates you can name as `baseTemplate`. The families are `offers`, `products`, `add_ons`, `pages`, `segments` and `discounts`. Read the family itself, without `recursive`: a template below it only names the attributes it uses.\n\nUse `POST /limio/jobs` instead for a bulk load of hundreds of items. Every create on this endpoint starts a catalog commit.\n\nThe platform writes the item before it indexes it. If the answer is a 5xx, read the item back before you retry: the write can have landed, and a retry then answers `422`.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogItemInput"}}}},"responses":{"200":{"description":"The platform created, committed and indexed the item.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"The item is not valid. `error.code` is `NO_RECORD_TYPE`, `NO_TEMPLATE` or `INVALID_ATTRIBUTE_SCHEMA`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogError"}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"},"422":{"description":"An item is already at this path. `error.code` is `EXISTING`. Change the item with `PUT /limio/catalogs/1/items/{path}` instead. Automation that retries can read this as work already done.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogError"}}}}}}}}}
```

## Update Catalog Item

> Changes the item at \`path\`. The platform reads the item, applies each change vector in \`cv\` in order, then writes the result.\
> \
> A change vector replaces the value at its path, so a partial object removes the attributes it leaves out. If you do not hold the current attributes, \`GET /limio/catalogs/1/items/{path}\` returns them; merge your change into them and send the complete object.\
> \
> Use \`PUT /limio/catalogs/1/bulk-update\` instead to change the same attribute on many items, for example a price change across a set of offers.\
> \
> A shop shows \`display\_price\_\_limio\` and \`detailed\_display\_price\_\_limio\`, not \`price\_\_limio\`. When you change the price of an offer, change these two attributes in the same request.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}}},"schemas":{"CatalogChangeVector":{"type":"object","description":"One change to apply to the item. The endpoint reads the item, applies every change vector in order, then writes the result. A field that no change vector names keeps its value.","properties":{"path":{"type":"array","description":"Where to apply the change, as a list of steps from the root of the item. The content of the item is under `data`, so an attribute change starts with `\"data\"`.\n\nA string step reads a field. A number step reads an array index. An object step selects one element of an array by its `name` field, for example `{ \"name\": \"monthly\" }`. No other key matches. Use an object step only as the last step. After it, a further step reads the first element of the array, not the selected one.","items":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"object"}]}},"value":{"description":"The new value at `path`. It replaces what is there, so send the complete object when `path` names an object. To change one attribute, read the item first and send the merged attributes."}},"required":["path","value"]},"CatalogError":{"type":"object","description":"The error contract of the catalog management endpoints. `error.code` names the reason, so automation can branch on it without reading the message.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"`NO_TEMPLATE` — the body has attributes but no `baseTemplate`, or the `baseTemplate` path does not exist. `INVALID_ATTRIBUTE_SCHEMA` — an attribute value does not agree with its definition in the template family. The message names the attribute and the rule that failed. `NO_RECORD_TYPE` — the path and the body together do not settle a record type. Send `record_type`. `EXISTING` — an item is already at this path. `NOTFOUND` — there is no item at this path."},"message":{"type":"string","description":"The reason in plain text."}}}}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/items/{path}":{"put":{"tags":["Catalog Management"],"operationId":"updateCatalogItem","summary":"Update Catalog Item","description":"Changes the item at `path`. The platform reads the item, applies each change vector in `cv` in order, then writes the result.\n\nA change vector replaces the value at its path, so a partial object removes the attributes it leaves out. If you do not hold the current attributes, `GET /limio/catalogs/1/items/{path}` returns them; merge your change into them and send the complete object.\n\nUse `PUT /limio/catalogs/1/bulk-update` instead to change the same attribute on many items, for example a price change across a set of offers.\n\nA shop shows `display_price__limio` and `detailed_display_price__limio`, not `price__limio`. When you change the price of an offer, change these two attributes in the same request.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cv":{"type":"array","description":"The changes to apply, in order.","items":{"$ref":"#/components/schemas/CatalogChangeVector"}}},"required":["cv"]}}}},"responses":{"200":{"description":"The platform applied the changes.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"The change is not valid. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogError"}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"}}}}}}
```

## Delete Catalog Item

> Deletes the item at \`path\`: an offer, a product, an add-on, a page, a segment or a discount.\
> \
> To delete more than one item in one request, send \`bulk\` as \`true\` and list the full paths in \`urls\`. The platform then ignores \`path\`. Send no body to delete the single item at \`path\`.\
> \
> A delete removes the item from the catalog and from the search index. It does not change subscriptions that already reference the item.\
> \
> The platform deletes the item before it re-indexes. If the answer is a 5xx, read the item back before you retry: the delete can have landed, and the read then answers \`404\`.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/items/{path}":{"delete":{"tags":["Catalog Management"],"operationId":"deleteCatalogItem","summary":"Delete Catalog Item","description":"Deletes the item at `path`: an offer, a product, an add-on, a page, a segment or a discount.\n\nTo delete more than one item in one request, send `bulk` as `true` and list the full paths in `urls`. The platform then ignores `path`. Send no body to delete the single item at `path`.\n\nA delete removes the item from the catalog and from the search index. It does not change subscriptions that already reference the item.\n\nThe platform deletes the item before it re-indexes. If the answer is a 5xx, read the item back before you retry: the delete can have landed, and the read then answers `404`.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"bulk":{"type":"boolean","description":"Set to `true` to delete the items listed in `urls` instead of the item at `path`."},"urls":{"type":"array","description":"The full catalog paths to delete. It is required when `bulk` is `true`.","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"The platform deleted the items.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"}}}}}}
```

## Duplicate Catalog Item

> Copies the item at \`path\` to a new item with the name in the body. The copy goes beside the source, in the same tree, and it keeps every attribute of the source.\
> \
> Use this endpoint to build a new item from one that is close to it: copy the offer, then change the price with \`PUT /limio/catalogs/1/items/{path}\`.\
> \
> The platform adds a suffix to the name if an item of that name is already in the tree. The response reports success only, so read the tree with \`GET /limio/catalogs/1/tree/{path}\` to learn the name that the copy has.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/items/duplicate/{path}":{"post":{"tags":["Catalog Management"],"operationId":"duplicateCatalogItem","summary":"Duplicate Catalog Item","description":"Copies the item at `path` to a new item with the name in the body. The copy goes beside the source, in the same tree, and it keeps every attribute of the source.\n\nUse this endpoint to build a new item from one that is close to it: copy the offer, then change the price with `PUT /limio/catalogs/1/items/{path}`.\n\nThe platform adds a suffix to the name if an item of that name is already in the tree. The response reports success only, so read the tree with `GET /limio/catalogs/1/tree/{path}` to learn the name that the copy has.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"The name of the copy."}},"required":["name"]}}}},"responses":{"200":{"description":"The platform created the copy.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"}}}}}}
```

## Get Catalog Tree

> Lists the items below \`path\`. The response comes from the catalog working tree, so it includes items that no publish has made live.\
> \
> Use this endpoint to learn what a tree holds: the offers under a path, the template families of a record type, or the name that a duplicate received. Give an empty path to list the top of the catalog.\
> \
> Do not use this endpoint to search by attribute or tag. \`GET /api/catalog\_items\` queries the search index, which filters and paginates.

```json
{"openapi":"3.1.0","info":{"title":"Limio Commerce API","version":"1.0.0"},"tags":[{"name":"Catalog Management","description":"Read and write one catalog item at a time, and list the trees that hold them. These endpoints work on the catalog itself, not on a search index. A read therefore answers with the record exactly as it is stored, and answers as soon as a write completes.\n\nUse them to read a record that you can name, to create, change, delete or copy a record, and to read a template family before a write. Use `PUT /limio/catalogs/1/bulk-update` to change the same attribute on many records in one request.\n\nTo find records by type, tag or attribute, use Catalog instead."}],"servers":[{"url":"https://{shard}.{domain}","variables":{"domain":{"default":"prod.limio.com"},"shard":{"default":"your-environment"}}}],"security":[{"bearerAuth":["catalog:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth 2.0 Bearer token for authenticated API access. Required for order submission, object management, and administrative operations.\n\nObtain a token via the OAuth flow described in [OAuth Bearer Token](https://docs.limio.com/developers/api-documentation/authentication-overview/oauth-bearer-token).\n\nPass the token in the `Authorization` header: `Bearer <token>`"}},"parameters":{"catalogItemPath":{"in":"path","required":true,"name":"path","description":"The catalog path of the item, without a leading slash. The last segment is the name of the item. The segments before it are the catalog tree of its record type. Encode each segment, and keep the slashes between them.\n\nEach record type has one tree: offers under `offers2`, pages under `pages2`, products under `products`, add-ons under `add_ons`, discounts under `discounts`, segments under `segments`, and template families under `config/templates`. The platform does not enforce the tree. An item written to the wrong tree is your responsibility.","schema":{"type":"string"}},"catalogItemRecursive":{"in":"query","name":"recursive","description":"Set to `true` to include every item below the path. The default response includes the direct children only.","schema":{"type":"boolean"}},"catalogItemWithDetails":{"in":"query","name":"withDetails","description":"Set to `true` to include the data of each item. The default response gives the metadata of the item only.","schema":{"type":"boolean"}}},"schemas":{"CatalogItem":{"type":"object","description":"An item, in the shape that a read returns.","properties":{"path":{"type":"string","description":"The full catalog path of the item, with a leading slash."},"name":{"type":"string"},"type":{"type":"string","description":"`item` for an item, `dir` for a tree node that holds other items.","enum":["item","dir"]},"id":{"type":"string","description":"A hash of the content of the item. The platform computes it on every read, so it changes when the item changes. It is not settable."},"version":{"type":"string","description":"The content version. The platform derives it. It is not settable."},"data":{"$ref":"#/components/schemas/CatalogItemInput"},"tree":{"type":"array","description":"The items below this path. It is empty when the item has no children.","items":{"type":"object"}}}},"CatalogItemInput":{"type":"object","description":"The content of the item. The platform stores it as you send it, after it validates the attributes.\n\n`id` and `version` are not settable. The platform derives both on every read, and it ignores values that you send under those names.","properties":{"name":{"type":"string","description":"The display name. Make it the same as the last segment of the path."},"record_type":{"type":"string","description":"The record type of the item.","enum":["add_on","discount","family","offer","page","product","segment","template"]},"baseTemplate":{"type":"string","description":"The path of the template that the item is built from. It is required when the body has `attributes`."},"attributes":{"type":"object","additionalProperties":true,"description":"The attribute values, keyed by the full attribute name including the `__limio` suffix. The platform checks each one against the type that the family gives it: a picklist against its options, and a string, number, boolean, array or object against that type. It does not check the shape inside an object, so a wrong shape there is stored as sent. It fills in the attributes that the family gives a default and you leave out.\n\nRead the template family first to learn which attributes exist and which values they accept. `GET /limio/catalogs/1/items/config/templates/offers` returns the offers family; the families are `offers`, `products`, `add_ons`, `pages`, `segments` and `discounts`.","properties":{"price__limio":{"type":"array","description":"The charges of an offer. The family does not give the shape of a charge, so read `OfferPrice` for it.","items":{"$ref":"#/components/schemas/OfferPrice"}}}},"products":{"type":"array","description":"The catalog paths of the products that an offer sells, for example `/products/Digital`. Send paths only: a read answers with each product expanded, but a write takes the path. An offer with no product sells nothing.","items":{"type":"string"}},"productBundles":{"type":"array","description":"The rate plan of each product in `products`, and its share of the price. Send one entry per product.","items":{"type":"object","properties":{"product_path":{"type":"string"},"rate_plan":{"type":"string"},"revenue_split":{"type":"string","description":"The share of the price, in percent."}}}}},"required":["record_type"]},"OfferPrice":{"type":"object","description":"One charge of an offer. `price__limio` holds an array of charges, and a subscription to the offer pays every charge in the array. The family types `price__limio` only as `price`, and the platform does not check the shape inside it. A wrong shape is stored as sent.\n\n`type` selects the charge. `recurring` bills `value` on each interval. `onetime` bills `value` one time. `recurringVolume` bills by quantity, from `tiers`. `usagePerUnit` bills `value` for each unit that the customer uses.\n\nA trial is two charges. The first charge bills the trial price with `repeat_count` set to the trial length. The second charge bills the full price, with `delay_interval` and `delay_interval_type` set to the same length, so that it starts when the trial ends.","properties":{"name":{"type":"string","description":"The identifier of the charge, unique in the array. The admin app writes `charge_1`, `charge_2` and so on."},"label":{"type":"string"},"type":{"type":"string","enum":["recurring","onetime","recurringVolume","usagePerUnit"]},"currencyCode":{"type":"string","description":"The ISO 4217 code of the currency."},"value":{"type":"string","description":"The amount, as a string. Every type has it except `recurringVolume`, which uses `tiers`."},"trigger":{"type":"string","description":"When the charge starts. `subscription_start` is the start date of the subscription. `order_date` is the date of the order. `delay` starts the charge after `delay_interval`.","enum":["subscription_start","order_date","delay"]},"delay_interval":{"type":"integer","description":"The length of the delay before the first bill of this charge."},"delay_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_interval":{"type":"integer","description":"The billing period of a recurring charge, or the term length of a termed `onetime` charge."},"repeat_interval_type":{"type":"string","enum":["days","weeks","months","years"]},"repeat_count":{"type":"integer","description":"The number of periods that the charge bills. Leave it out, or send 0, to bill until the subscription ends."},"term_setting":{"type":"string","description":"Only for `onetime`. `termed` gives access for `repeat_count` terms of `repeat_interval`. `evergreen` has no end date. `line_item` is a purchase that gives no access period.","enum":["termed","evergreen","line_item"]},"tiers":{"type":"array","description":"Only for `recurringVolume`. The tiers must cover the units from 1 without a gap. Leave out `ending_unit` on the last tier.","items":{"type":"object","properties":{"starting_unit":{"type":"integer"},"ending_unit":{"type":"integer"},"price":{"type":"string"},"price_format":{"type":"string","description":"`PerUnit` bills `price` for each unit. `FlatFee` bills `price` one time for the tier.","enum":["PerUnit","FlatFee"]}},"required":["starting_unit","price","price_format"]}},"use_external_price":{"type":"boolean","description":"When true, the billing system prices the charge, and `value` is shown only."}},"required":["name","type","currencyCode"]}},"responses":{"401ApplicationResponse":{"description":"Unauthorized: the request lacks valid authentication credentials. Verify your Bearer token or API key is correct and has not expired.","content":{"application/json":{"schema":{"type":"object","properties":{"requestId":{"type":"string","description":"The unique identifier of the request"},"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"paths":{"/limio/catalogs/1/tree/{path}":{"get":{"tags":["Catalog Management"],"operationId":"getCatalogTree","summary":"Get Catalog Tree","description":"Lists the items below `path`. The response comes from the catalog working tree, so it includes items that no publish has made live.\n\nUse this endpoint to learn what a tree holds: the offers under a path, the template families of a record type, or the name that a duplicate received. Give an empty path to list the top of the catalog.\n\nDo not use this endpoint to search by attribute or tag. `GET /api/catalog_items` queries the search index, which filters and paginates.","parameters":[{"$ref":"#/components/parameters/catalogItemPath"},{"$ref":"#/components/parameters/catalogItemRecursive"},{"$ref":"#/components/parameters/catalogItemWithDetails"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"tree":{"type":"array","description":"The items below the path.","items":{"$ref":"#/components/schemas/CatalogItem"}}}}}}},"401":{"$ref":"#/components/responses/401ApplicationResponse"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.limio.com/api/catalog-api/catalog-management.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
