Set cart item quantity
Set a line's quantity, or adjust it by ±1.
Cart
/api/v1/scent/cart/{customerRef}/items/{variantId}Updates an existing line in the cart identified by the required customerRef path segment.
Supply EXACTLY ONE of: quantity (sets the absolute quantity, must be >= 1) or action — a
relative ±1 adjustment, increment (+1) or decrement (-1). A decrement that reaches 0
removes the line entirely, and if that empties the cart the cart row itself is deleted (the
same D31 behavior as DELETE .../items/{variantId}). increment re-checks product access
and availability (it is new demand); a decrease can never oversell. Supplying neither, or
both, is 400 VALIDATION_ERRORS. The line must already exist — an increment/decrement/set
of a variant not in the cart is 404 NOT_FOUND_CART_ITEM. There is no separate decrement
endpoint — this single PUT covers both the absolute-set and the ±1-adjustment shapes.
Path parameters
variantIdstring (uuid)RequiredcustomerRefstringRequiredThe per-customer cart/registration selector this endpoint operates on (max 128 chars, URL-encoded) — every cart/wishlist/stock-notification endpoint carries it in the path (API Docs Review effort, Phase F/G, D29/R-CUSTOMERREF). Also the value a per-customer redemption cap, if any, is checked against.
Body parameters
quantityintegerNew absolute quantity for the line (>= 1). Required when no action is supplied; must be omitted when action is set.
actionstring (enum)A relative ±1 adjustment — increment (+1, new demand) or decrement (-1; reaching 0 removes the line). Omit to set an absolute quantity instead.
Response
idstring (uuid)Null when there is no persisted cart row yet (a lazy-create GET/preview never creates one).
customerRefstringstatusstring (enum)None is a RESPONSE-ONLY value for the 200-empty (no cart row) representation.
baseCurrencystringHoisted from each line’s own currency (the Scent catalog is SAR-only).
itemsarray of objecttotals.chargedWithVatnumber (decimal)Sum of every line’s goods-sale total including VAT, PLUS the order-level shipping fee — what checkout would actually charge.
totals.commissionWithVatnumber (decimal)Sum of every CREDITABLE (Retail-mode) line’s commission including VAT — 0 for an all-Wholesale cart, never null.
totals.netAfterCommissionnumber (decimal)chargedWithVat minus commissionWithVat.
totals.shippingobjecttotals.displayobjectPopulated only for a supported non-SAR X-Partner-Currency.
pricedAtstring (date-time)The server UTC time of THIS read; null for the 200-empty representation.
Other responses
400Request validation failed.401No bearer token (UNAUTHORIZED_MISSING_TOKEN), an invalid token (UNAUTHORIZED_INVALID_TOKEN) or an expired one (UNAUTHORIZED_TOKEN_EXPIRED — mint a new token). UPSTREAM_AUTH_FAILED is different: a dependent service refused the Gateway itself. That is a server-side fault — retry later and do NOT re-mint your token. See the Errors guide.403The route is not available to your credential (FORBIDDEN).404Unknown variant or no such line.415The request body is not sent as application/json (UNSUPPORTED_MEDIA_TYPE).429Too many requests — see the Rate limits guide. Retry after the Retry-After header (seconds).502A dependent service returned an invalid response (BAD_GATEWAY). Retry with backoff.503The service is temporarily unavailable (SERVICE_UNAVAILABLE). Retry with backoff.504A dependent service did not respond in time (GATEWAY_TIMEOUT). Retry with backoff.curl --request PUT \ --url https://api-omni.linra.net/api/v1/scent/cart/value/items/5b1e3a1c-9d2e-4f3a-8b1c-9f0a2e3d4c5b \ --header 'Authorization: Bearer $TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "quantity": 1, "action": "increment" }'const res = await fetch('https://api-omni.linra.net/api/v1/scent/cart/value/items/5b1e3a1c-9d2e-4f3a-8b1c-9f0a2e3d4c5b', { method: 'PUT', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ quantity: 1, action: 'increment' }),});const { payload } = await res.json();var response = await httpClient.PutAsJsonAsync( "https://api-omni.linra.net/api/v1/scent/cart/value/items/5b1e3a1c-9d2e-4f3a-8b1c-9f0a2e3d4c5b", new { quantity = 1, action = "increment" });
var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<CartResponse>>();Response
{ "state": "SUCCESS", "payload": { "id": "5b1e3a1c-9d2e-4f3a-8b1c-9f0a2e3d4c5b", "customerRef": "string", "status": "None", "baseCurrency": "SAR", "items": [ { "variantId": "5b1e3a1c-9d2e-4f3a-8b1c-9f0a2e3d4c5b", "sku": "string", "sizeMl": 0, "quantity": 0, "pricing": { "currency": "SAR", "unitPrice": 0, "line": { "subtotal": 0, "goodsVatAmount": 0, "totalWithVat": 0, "commission": { "amount": null, "vatAmount": null, "netAfterCommission": null, "rate": null } }, "rrp": 0, "display": { "unitPrice": 0, "line": { "subtotal": null, "goodsVatAmount": null, "totalWithVat": null, "commission": null }, "rrp": 0 } }, "availability": { "state": "Available", "availableQuantity": 0, "insufficientForQuantity": true }, "marketing": { "currentPrice": 0, "compareAtPrice": 0, "discountPercent": 0, "badges": [ "LIMITED" ], "currentPriceDisplay": 0, "compareAtPriceDisplay": 0 } } ], "totals": { "chargedWithVat": 0, "commissionWithVat": 0, "netAfterCommission": 0, "shipping": { "amount": 0, "vatAmount": 0, "amountWithVat": 0, "freeThreshold": 0, "isFree": true }, "display": { "currency": "string", "rate": 0, "chargedWithVat": 0, "commissionWithVat": 0, "netAfterCommission": 0, "shipping": { "amount": 0, "vatAmount": 0, "amountWithVat": 0, "freeThreshold": 0 } } }, "pricedAt": "2026-08-22T09:14:03Z" }, "details": null}
