[
  {
    "date": "2026-11-02",
    "api": "V1",
    "type": "Added",
    "title": "Item variants can now be created through the API",
    "endpoints": [
      "POST api/v1/Item",
      "POST api/v1/ItemBulk"
    ],
    "description": "**itemVariants** entries with a **variantCode** the item does not have yet are now created, on `POST api/v1/Item` (also for an item that this call creates) and on `POST api/v1/ItemBulk`. Each entry takes **variantCode** (required, up to 200 characters), **variantDescription** (up to 400 characters, defaults to the variantCode), **quantityOnHand**, **productCode** (up to 50) and **refNumber** (up to 400). For a variant the item already has, only **quantityOnHand** changes, as before: its description, product code, reference number and active status are not changed through the API, and an inactive variant stays inactive. An invalid entry (a missing or too long value, the same variantCode twice, more than 500 variants) rejects the update of that item before anything is saved; on `POST api/v1/ItemBulk` the item gets **updateStatus** ERROR with the reason in **updateMessage**. On `POST api/v1/ItemBulk` **updateMessage** now also tells what happened to the variants, for example `Updated. Variants: 1 created, 2 stock updated.`, and a failure while saving variants is reported on the item instead of being ignored. The list rules are unchanged: `POST api/v1/ItemBulk` treats **itemVariants** (with **itemVariantsSpecified: true**) as the item's complete set, so active variants that are not in it are deactivated; `POST api/v1/Item` leaves variants that are not in the request untouched."
  },
  {
    "date": "2026-10-29",
    "api": "V1",
    "type": "Breaking",
    "title": "shippingWeight and shippingWeightUnit removed from item bulk update",
    "endpoints": [
      "POST api/v1/ItemBulk"
    ],
    "description": "`POST api/v1/ItemBulk` no longer accepts **shippingWeight** and **shippingWeightUnit** (with **shippingWeightSpecified** / **shippingWeightUnitSpecified**); they are ignored if sent. Send the shipping weight of each unit of measure in **unitCodes[].packaging** instead (**weight** with **weightUnit**), which is what invoices, picking tickets and delivery documents use."
  },
  {
    "date": "2026-10-29",
    "api": "V1",
    "type": "Added",
    "title": "Shipping packaging per unit of measure on items",
    "endpoints": [
      "GET api/v1/Item",
      "GET api/v1/ItemList",
      "GET api/v1/ItemAllList",
      "POST api/v1/Item",
      "POST api/v1/ItemBulk"
    ],
    "description": "Each unit of measure in **unitCodes** now carries its shipping packaging in **packaging**, used for the weight and volume totals on invoices, picking tickets and delivery documents. **packaging** has **length**, **width**, **height** with **dimensionUnit** (`IN`, `FT`, `CM`, `M`), **volume** with **volumeUnit** (`CUFT`, `CUIN`, `CBM`; leave **volume** empty to calculate it from the dimensions), **weight** with **weightUnit** (`LB`, `KG`, `OZ`, `G`), **packagingWeight** (in **weightUnit**: the weight of a larger unit's own packaging, such as a carton or pallet, added to the base unit weight multiplied by the base count when the unit has no **weight** of its own), **isStackable** and **maxStackCount**. It is returned by `GET api/v1/Item`, `GET api/v1/ItemList` and `GET api/v1/ItemAllList` (omitted for units without packaging). `POST api/v1/Item` and `POST api/v1/ItemBulk` accept it: a unit sent with **packaging** gets it replaced with the values sent, an empty **packaging** `{}` clears it, and units sent without **packaging** keep theirs. In `POST api/v1/ItemBulk` the packaging is saved even when **unitCodesSpecified** is `false`, so you can change only the packaging without replacing the units. A unit that is not a unit of the item, an incomplete set of dimensions or an unknown unit fails the item with a message in **updateMessage**."
  },
  {
    "date": "2026-10-21",
    "api": "V1 & C1",
    "type": "Added",
    "title": "Post free-text orders into the OrderSense Inbox",
    "endpoints": [
      "POST api/v1/OrderSenseMessage",
      "POST api/c1/OrderSenseMessage"
    ],
    "description": "Orders written as plain text can now be sent to OrderSense through the API and reviewed in the OrderSense Inbox like a text message or email. `POST api/v1/OrderSenseMessage` takes **text** (the order as the customer wrote it, required), optional **subject**, **externalId** (your own id of the order; the same id sent again is stored once and answered with status `duplicate`) and **senderName**, and names the customer with any of **customerId**, **accountNumber**, **phone**, **email** or **customerName**: the first that matches exactly one active customer is set on the message, anything else is shown to the reviewer as a hint. `POST api/c1/OrderSenseMessage` takes the same **text**, **subject**, **externalId** and **senderName** for the customer the access token belongs to. Both answer `data` with **messageId**, **status** (`queued` or `duplicate`), **customerId**, **customerName** and **customerMatchedBy** (`ID`, `ACCOUNT`, `PHONE`, `EMAIL`, `NAME`, or `TOKEN` for C1), or `updateStatus` `ERROR` with **updateMessage** when OrderSense is not switched on for the company or the request is incomplete."
  },
  {
    "date": "2026-10-07",
    "api": "V1",
    "type": "Fixed",
    "title": "Sales order update no longer clears item variants when an empty list is sent",
    "endpoints": [
      "POST api/v1/SalesOrder"
    ],
    "description": "When `POST api/v1/SalesOrder` updates an existing order, a line sent with an empty **itemVariants** array (`\"itemVariants\": []`) no longer clears the variant selection stored on that line. Omitted, `null` and empty **itemVariants** all leave the line's variants unchanged, in line with the update rule that fields cannot be cleared; send a non-empty **itemVariants** array to replace the selection. Previously an empty array (the default many client libraries send for an unset collection) wiped the stored variants."
  },
  {
    "date": "2026-09-06",
    "api": "V1",
    "type": "Added",
    "title": "MCP access token endpoint for AI assistant connections",
    "endpoints": [
      "POST api/v1/GetMcpAccessToken"
    ],
    "description": "AI assistants can now connect to LodDepot through the new MCP (Model Context Protocol) server, which authenticates with dedicated MCP Access credentials. `POST api/v1/GetMcpAccessToken` exchanges an MCP Access credential set (**companyCode**, **clientId**, **clientSecret**, optional **accountId**) for a 60-minute bearer token, the same way `POST api/v1/GetAccessToken` does for API Access credentials. MCP Access credentials are created in the Portal under Settings, API / AI Access, AI Assistant (MCP) Access, and are separate from API Access credentials: each credential type is only accepted by its own token endpoint, so MCP access can be granted and revoked without affecting existing API integrations. The returned token is a standard V1 bearer token accepted by all `api/v1` endpoints."
  },
  {
    "date": "2026-08-31",
    "api": "V1",
    "type": "Fixed",
    "title": "ItemBulk now updates variant stock on hand",
    "endpoints": [
      "POST api/v1/ItemBulk"
    ],
    "description": "`POST api/v1/ItemBulk` now applies the **itemVariants** array that was previously ignored on this endpoint. Send **itemVariantsSpecified: true** together with **itemVariants**, where each entry needs **variantCode** and **quantityOnHand**, e.g. `\"itemVariantsSpecified\":true,\"itemVariants\":[{\"variantCode\":\"46\",\"quantityOnHand\":13}]` — the variant's stock quantity is updated the same way `POST api/v1/Item` does it. On `POST api/v1/ItemBulk` the list is treated as the complete set of the item's variants: variants NOT included in the list are deactivated (an empty list with **itemVariantsSpecified: true** deactivates all of the item's variants), so only send **itemVariantsSpecified: true** when the full variant list is provided. Only the stock quantity and active status of existing variants change: other variant fields are left unchanged, entries with an unknown **variantCode** are ignored, and variants cannot be created or deleted through the API."
  },
  {
    "date": "2026-08-10",
    "api": "V1",
    "type": "Added",
    "title": "Item black/white list mode",
    "endpoints": [
      "GET api/v1/ItemList",
      "GET api/v1/ItemAllList",
      "GET api/v1/Item/{id}",
      "POST api/v1/Item",
      "POST api/v1/ItemBulk"
    ],
    "description": "Items now expose a **blackListMode** field controlling how the item's customer/user list is applied: **BLACK** (default) hides the item from the customers and users on the list, **WHITE** makes the item visible ONLY to the customers and users on the list (with an empty list, a WHITE item stays visible to everyone; WHITE items are always hidden on public catalog links). Item responses include **blackListMode**; `POST api/v1/Item` accepts it to set the mode, and `POST api/v1/ItemBulk` accepts **blackListMode** with **blackListModeSpecified: true** to change it for many items at once. The list itself is still managed with the existing **customerBlackListIdsAdd** / **customerBlackListIdsRemove** / **customerBlackListClear** fields."
  },
  {
    "date": "2026-08-10",
    "api": "V1 & C1",
    "type": "Changed",
    "title": "Item variant stock on hand readable and updateable",
    "endpoints": [
      "GET api/v1/ItemList",
      "GET api/v1/ItemAllList",
      "GET api/v1/Item/{id}",
      "POST api/v1/Item",
      "GET api/c1/ItemList"
    ],
    "description": "The **itemVariants** array on item responses now includes **quantityOnHand** — the variant's stock on hand (omitted when not set). `POST api/v1/Item` now accepts an **itemVariants** array to update variant stock: each entry needs **variantCode** and **quantityOnHand**, e.g. `\"itemVariants\":[{\"variantCode\":\"BGL\",\"quantityOnHand\":25}]`. Only the stock quantity of existing variants is updated — other variant fields are left unchanged, entries with an unknown **variantCode** are ignored, and variants cannot be created or deleted through the API (variants are managed in the LodDepot portal). Variants not included in the request are left untouched."
  },
  {
    "date": "2026-08-06",
    "api": "V1",
    "type": "Changed",
    "title": "POST api/v1/SalesOrder now updates existing sales orders",
    "endpoints": [
      "POST api/v1/SalesOrder"
    ],
    "description": "`POST api/v1/SalesOrder` now performs an update when **orderId** or **salesOrderGuid** identifies an existing order (when both are sent, **orderId** wins); requests without an identifier keep creating new orders. Update rules: omitted fields are left unchanged (fields cannot be cleared); **customer** is ignored on update — an order cannot be moved to another customer; **items** are merged by **itemId** — a matching line is updated, an unmatched line is added, a line sent with **quantityUnit: 0** is removed, and lines not included in the request are left untouched (omit **items** entirely for a header-only update); **address**, when provided, replaces the ship-to address (send an empty address object `{}` to remove it, omit to keep it). An unknown **orderId** returns an error; an unknown **salesOrderGuid** still creates a new order with that ID. Note: previously a request re-using an existing **salesOrderGuid** returned an error — such a request now updates that order. Updates trigger the **SALESORDER.UPDATE** webhook and ERP re-sync."
  },
  {
    "date": "2026-07-29",
    "api": "V1 & C1",
    "type": "Added",
    "title": "Item variants available on items and sales order lines",
    "endpoints": [
      "GET api/v1/ItemList",
      "GET api/v1/ItemAllList",
      "GET api/v1/Item/{id}",
      "GET api/v1/SalesOrder/{id}",
      "GET api/v1/SalesOrderList",
      "POST api/v1/SalesOrder",
      "GET api/c1/ItemList",
      "GET api/c1/SalesOrder/{id}",
      "GET api/c1/SalesOrderList"
    ],
    "description": "Item responses now include the read-only **itemVariants** field — an array of the item's active variants: `[{\"itemVariantId\":123,\"variantCode\":\"BGL\",\"variantDescription\":\"Brooklyn Gray L\",\"quantityOnHand\":25.0,\"productCode\":\"012345\",\"refNumber\":\"...\"}]` (omitted when the item has no variants; variants are managed in the LodDepot portal). Sales order line items now include an **itemVariants** array holding the selected variants with quantities: `[{\"variantCode\":\"BGL\",\"variantDescription\":\"Brooklyn Gray L\",\"quantity\":2}]` — **variantCode** is the variant identifier on order lines. `POST api/v1/SalesOrder` accepts **itemVariants** on order lines in the same format; quantities are in the line's selling unit."
  },
  {
    "date": "2026-07-13",
    "api": "V1",
    "type": "Added",
    "title": "Item warning text available in the Item API",
    "endpoints": [
      "GET api/v1/ItemList",
      "GET api/v1/ItemAllList",
      "GET api/v1/Item/{id}",
      "POST api/v1/Item"
    ],
    "description": "Item responses now include the **itemWarningText** field containing the product warning text entered in LodDepot (Item Information/Warning Text; HTML allowed, returned exactly as entered; null when not set). `POST api/v1/Item` also accepts **itemWarningText** on create and update — omitting the field leaves the stored value unchanged. Bulk updates via `POST api/v1/ItemBulk` already supported this field."
  }
]