Skip to content

Endpoint reference

REST 1.5 has 126 routes: 51 GET and 75 writes, including 67 inventory-planning routes (17 GET, 50 writes). The tables below are a quick scan of the routes, their filters and what they return. For full request and response schemas, the interactive explorer is the richer view.

The full machine-readable contract is served publicly (no auth, no shop data) at:

GET https://prod.logistified.app/api/v1/openapi.json

Point Postman, Insomnia, or any OpenAPI 3.1 client at that URL to generate a typed client or browse every parameter and response shape.

CollectionRoutesGETWrites
Purchase orders401525
Stock takes440
Stock adjustments330
Transfer orders770
Manufacturing orders330
Forecast views220
Inventory commitments13310
New product plans23617
Sales plans15411
Promotional events1129
Promotional event catalogue110
Inventory planning options413
Total1265175

GET endpoints need a read-scope bearer token (a write key works too). POST / PATCH / DELETE need a write-scope token. Paths below are under /api/v1. Writes answer 200 unless the row says 201.

PathReturns
GET /purchase-ordersList (filters: status, supplierId, search, dateField, dateFrom, dateTo, paidStatus, locationId, variantId, sku, limit, cursor). dateField picks the date that dateFrom / dateTo filter
GET /purchase-orders/{id}Full detail: header, line items, shipment, addresses, payments, documents
GET /purchase-orders/{id}/line-itemsLine items, each with its delivery schedules[]
GET /purchase-orders/{id}/paymentsPayments
GET /purchase-orders/{id}/documentsDocument metadata
GET /purchase-orders/{id}/shipmentShipment (the first one)
GET /purchase-orders/{id}/shipmentsAll shipments, each with its line allocations
GET /purchase-orders/{id}/addressesBilling + shipping addresses
GET /purchase-orders/{id}/receipt-scheduleReceiving / incoming schedule
GET /purchase-orders/{id}/cost-sync-historyCost-sync history: every unit-cost update this order proposed, with its outcome (?variantIds=a,b narrows). “Proposed” and “failed” rows may still have been applied
GET /purchase-orders/{id}/logActivity log
GET /purchase-orders/{id}/creditsApplied + available supplier credits
PathDoes
POST /purchase-ordersCreate a draft. 201 + Location. An empty body {} is a valid empty draft
PATCH /purchase-orders/{id}Update header fields (at least one). status is not accepted here
DELETE /purchase-orders/{id}Delete a Draft outright, or soft-delete a Cancelled order
POST /purchase-orders/{id}/line-itemsAdd catalog line items (bulk, 1–500). 201
POST /purchase-orders/{id}/line-items/customAdd one non-catalog line. 201
PATCH /purchase-orders/{id}/line-itemsUpdate line items (bulk, 1–500). updateSupplier: true also writes the supplier-related values back to the supplier’s catalogue
PATCH /purchase-orders/{id}/line-items/schedulesReplace the delivery schedule of the named lines (lines[], 1–200 lines, 1–50 deliveries each). Rows you omit are removed
PATCH /purchase-orders/{id}/line-discountsApply or clear a per-unit percentage or fixed discount on selected lines (updates[]), all or nothing
PATCH /purchase-orders/{id}/addresses/{addressType}Set the order’s billing, shipping or supplier address: a saved profileId, or custom address fields
DELETE /purchase-orders/{id}/line-items?lineItemIds=a,bDelete line items (bulk). Can partly succeed; check failed[]
POST /purchase-orders/{id}/statusMove the order’s status
POST /purchase-orders/{id}/confirmRecord confirmed quantities: lines or confirmAll
POST /purchase-orders/{id}/receiveRecord received quantities (absolute totals)
POST /purchase-orders/{id}/unreceiveTake back received quantities (deltas)
POST /purchase-orders/{id}/completeComplete the order and run the cost sync
POST /purchase-orders/{id}/quick-completeReceive + sync + complete, in one call from In Progress
POST /purchase-orders/{id}/shopify-syncPush received quantities to Shopify
POST /purchase-orders/{id}/shopify-sync/resetClear the sync bookkeeping (Shopify untouched)

Reusable billing (company) and shipping addresses for new purchase orders. Editing or archiving a profile never rewrites the address already stored on an order.

PathDoes
GET /purchase-order-address-profilesList profiles. Archived ones are hidden unless includeArchived=true
POST /purchase-order-address-profilesSave a profile (name, addressType, address). Send profileId to update an existing one. 200
POST /purchase-order-address-profiles/{profileId}/archiveArchive a profile so it can’t be picked for new orders
POST /purchase-order-address-profiles/{profileId}/restoreRestore an archived profile
GET /suppliers/{supplierId}/address-profile-defaultsThe supplier’s default billing and shipping profile ids
PATCH /suppliers/{supplierId}/address-profile-defaults/{addressType}Set the supplier’s default profile for that type; { "profileId": null } clears it

Follow-up actions on the unit-cost updates a completed order sends to Shopify and to supplier catalogues. Each write takes an optional body requestKey: reuse it only to recover a lost reply, and use a new one after a refusal. These routes can refuse with their own error codes; see Errors.

PathDoes
POST /purchase-orders/{id}/resync-costsRe-send the costs of a Completed order that has cost tracking (optional targets: shopify, supplier). Per-target results[]
POST /purchase-order-cost-targets/{targetKey}/adoptResolve an unconfirmed update by adopting the target’s current cost (optional variantId)
POST /purchase-order-cost-updates/{opId}/cancelCancel a proposed update that was never picked up and is more than an hour old
GET /purchase-order-cost-targets/{targetKey}/historyOne cost target’s history and live state, including whether adopt or cancel is possible (?variantId=)

URL-encode the targetKey in the path (it contains colons, for example supplier%3A…).

Read-only.

PathReturns
GET /stock-takesList (filters: status, locationId, search, limit, cursor)
GET /stock-takes/reasonsConfigured stock-take reasons
GET /stock-takes/{id}Full detail: header + line items
GET /stock-takes/{id}/line-itemsLine items

Read-only.

PathReturns
GET /stock-adjustmentsList (filters: status, locationId, reason, createdBy, search, limit, cursor)
GET /stock-adjustments/{id}Full detail: header + line items
GET /stock-adjustments/{id}/line-itemsLine items

Adjustments are the delta-only sibling of stock takes: a line carries a signed delta (and the resulting value change), never a counted quantity. The header carries the operator (createdBy), the live netQuantityChange, and the revertOf / revertedBy links when the document is part of a revert pair. Reasons are shared with stock takes; read them from GET /stock-takes/reasons.

Read-only. A commitment can create a linked transfer order through POST /inventory-commitments/{id}/transfer-orders.

PathReturns
GET /transfer-ordersList (filters: status, search, dateField, dateFrom, dateTo, fromLocationId, toLocationId, variantId, sku, limit, cursor)
GET /transfer-orders/{id}Full detail: header (incl. line items), addresses, log
GET /transfer-orders/{id}/line-itemsLine items
GET /transfer-orders/{id}/shipmentShipment
GET /transfer-orders/{id}/documentsDocument metadata
GET /transfer-orders/{id}/addressesFrom + to addresses
GET /transfer-orders/{id}/logActivity log

Read-only.

PathReturns
GET /manufacturing-ordersList (filters: status (repeatable), executionMode, productionLocationId (repeatable), search, dateField, dateFrom, dateTo, includeMeasures, limit, cursor)
GET /manufacturing-orders/{id}Full detail: items to build, materials, and the linked purchase orders / transfers / stock releases, plus computed progress when includeMeasures was requested
GET /manufacturing-orders/{id}/logActivity log

Repeat a repeatable filter instead of joining values with commas: ?status=DRAFT&status=IN_PROGRESS.

Read-only. A forecast view is a saved forecast scope you configure in the app (locations, filters, columns, sort). The rows endpoint returns what the view shows: the response carries a columns[] descriptor (id, displayName (the column header in the app), type (string / number / boolean / date), optional unit (currency marks a money column; its currencyColumn names the row key holding that row’s currency code, null meaning the response’s currencyCode) and, for supply-mode views, period) and every row is an object keyed by those ids, plus variantId and productId on every row. Values are the table’s display values as JSON (dates as YYYY-MM-DD, blanks as null). Read columns[] on every page; the view’s columns may change whenever someone edits the view in the app.

PathReturns
GET /forecast-viewsList of saved views: viewId, viewName, isStarred, isAutogenerated. Autogenerated (dashboard) views are hidden unless includeAutogenerated=true
GET /forecast-views/{id}/rowsThe view’s rows, view-shaped: currencyCode, columns[], rows[], totalCount, cursor (params: limit (up to 1000, default 50), cursor, variantIds CSV)

Three plan collections (inventory commitments, new product plans, sales plans), promotional events with their holiday catalogue, and the planning options. For a worked example of each, see the inventory planning walkthrough.

The three plan collections share these routes and query parameters:

  • List (GET /{collection}): inventoryPlans[] summary headers, totalCount, cursor. Filters: statuses and locationIds (comma-separated), search, variantId, createdFrom / createdTo, a type-specific date range (below), sort (name, number, status, target, window, launch, created, updated), dir (asc, desc; default desc), limit (up to 50) and cursor.
  • Detail (GET /{collection}/{id}): inventoryPlan (the header with summary counts). The header carries no money, so there is no currencyCode; items (with prices and their currencyCode) are never included, page them separately.
  • Items (GET /{collection}/{id}/items): items[], totalCount, cursor, currencyCode. Optional locationId and variantId filters; includeCurves=true adds the daily curves. limit defaults to and allows at most 250, or 50 with includeCurves=true.
  • Status (POST /{collection}/{id}/status): { "status": "Confirmed" }, one of Draft, Confirmed, Locked, Completed, Cancelled. Returns inventoryPlan and transition (from, to). Changing a status never moves stock.
  • Delete (DELETE /{collection}/{id}): deletes the plan and its items. Returns deleted and inventoryPlanId.
  • Bulk item delete (DELETE /{collection}/{id}/items?itemIds=a,b): 1–500 item ids in the query; the body is ignored. Returns deletedCount and deletedItemIds.

An id from another plan collection, a deleted plan or an item of another plan all return the same 404 NOT_FOUND.

Known or possible future sales. List date filter: targetDateFrom / targetDateTo (the end date, falling back to the target date, then the start date).

Method + pathDoes
GET /inventory-commitmentsList
GET /inventory-commitments/{id}Header with counts, probability, dates, recipient, linked orders (purchaseOrderIds, linkedOrders, transferOrderId)
GET /inventory-commitments/{id}/itemsItem page
POST /inventory-commitmentsCreate a Draft. Header fields plus optional lineItems[] (variantId, plannedUnits, locationId; up to 500). 201 + Location. Returns inventoryPlanId, planNumber, skipped
PATCH /inventory-commitments/{id}Edit header fields, including includedInForecast. null clears a date or the recipient. Returns inventoryPlan
POST /inventory-commitments/{id}/statusChange status
DELETE /inventory-commitments/{id}Delete the plan
POST /inventory-commitments/{id}/itemsAdd items[] (variantId, plannedUnits, locationId; 1–500). 201. Returns addedCount, skipped
PATCH /inventory-commitments/{id}/items/{inventoryPlanItemId}Edit one item: plannedUnits, probability, locationId, targetDate / startDate / endDate, notes. null on probability restores the header value; null on a date clears it, and the item takes the header dates once none of its own remain. Returns item, currencyCode
DELETE /inventory-commitments/{id}/items?itemIds=a,bBulk item delete
POST /inventory-commitments/{id}/forecast-feed{ "includedInForecast": false } pauses the plan’s demand in the forecast, true resumes it. Works while Locked. Returns inventoryPlanId, includedInForecast
POST /inventory-commitments/{id}/purchase-ordersCreate one draft purchase order per supplier: locationId (receiving), quantityBasis (planned or expected), optional supplierIds. Returns purchaseOrders[], failed[], linkFailures[], unassignedVariantIds; can be a partial 200
POST /inventory-commitments/{id}/transfer-ordersCreate one linked draft transfer order: fromLocationId, toLocationId, quantityBasis. Returns transferOrderId, transferOrderNumber, optional linkFailure
  • Header fields: name, demandType, reasonCode, source, notes, probability, justification, targetDate, startDate, endDate, allocationMethod (single, even), recipient.
  • Timing: a commitment has either a targetDate or a startDate + endDate range, not both. A PATCH that changes targetDate clears the range, and one that changes the range clears targetDate. Confirming needs a header date (PS_HEADER_DATE_REQUIRED) and at least one item (EMPTY_PLAN).
  • Quantities: expectedUnits is plannedUnits × probability (the item’s own, or the header’s). quantityBasis: "expected" orders those units; planned orders plannedUnits. Each variant’s units are summed across its locations into one order line.
  • Orders: both order actions work in every status. Suppliers come from each variant’s primary supplier; variants without one are listed in unassignedVariantIds. A plan has one linked transfer order: a second call while it exists is refused with TRANSFER_ORDER_ALREADY_LINKED.

Launch demand built from similar products. List date filter: launchDateFrom / launchDateTo.

Method + pathDoes
GET /new-product-plansList
GET /new-product-plans/{id}Header with counts, launchDate, horizonDays, volume, feedsForecast, includedInForecast, locations[], linked orders and sizeGroups[]
GET /new-product-plans/{id}/itemsItem page
GET /new-product-plans/{id}/plan-vs-actualDaily plan against actual sales: asOf, aggregate, items[], offPlan[] (not with locationId), warnings. Optional locationId; collapse=variant combines a variant’s locations and adds cells[] and perLocation[]
GET /new-product-plans/{id}/order-quantityOrder-quantity preview, nothing created: lines[] (one per item and location) and totalQuantity. Query: coverageDays, coverFrom (launch, today), safetyFactor, moq, packSize, minOnePerSize
GET /new-product-plans/{id}/size-groups/{groupId}/actual-mixActual sales shares of a size group’s sizes since launch, with the gate it must pass (gateMinDays, gateMinUnits). includeFamily=true adds the group’s other locations (siblings[]) and an all-locations comparison
POST /new-product-plansCreate a Draft: launchDate, horizonDays, items[] (1–200); optional name, taxonomy, notes, locationIds (or "all"), volume, feedsForecast, autoStopOnFirstSale, sizeGroups[]. 201 + Location. Returns inventoryPlanId, planNumber, warnings, skipped
PATCH /new-product-plans/{id}Edit name, taxonomy, notes, launchDate, horizonDays, volume, feedsForecast, includedInForecast, autoStopOnFirstSale. Returns inventoryPlan
POST /new-product-plans/{id}/statusChange status
DELETE /new-product-plans/{id}Delete the plan
POST /new-product-plans/{id}/itemsAdd items[] (1–200) and optional sizeGroups[]. A plan with locations adds each variant at every location. 201. Returns addedCount, skipped, warnings, historyWarnings, evenShareItemIds
PATCH /new-product-plans/{id}/items/{inventoryPlanItemId}Edit one item: locationId, launchDate, sharePercent, volume, similarProducts, curve. Returns item, currencyCode
DELETE /new-product-plans/{id}/items?itemIds=a,bBulk item delete
POST /new-product-plans/{id}/forecast-feedSet includedInForecast (pause or resume). Works while Locked. Returns inventoryPlanId, includedInForecast
POST /new-product-plans/{id}/size-groupsCreate a size group: similarProducts (required), optional name, productId, volume, horizonDays, members[] (variantId, sharePercent), keepEdited. Creates one copy per plan location. 201. Returns groupIds, familyId, skipped, keptEdited, overwritten
PATCH /new-product-plans/{id}/size-groups/{groupId}Edit that one location’s copy of the group. Omitted similarProducts / members keep their stored values
DELETE /new-product-plans/{id}/size-groups/{groupId}Delete the group’s whole family at every location, with its sizes. Returns removedGroupIds
POST /new-product-plans/{id}/size-groups/{groupId}/apply-mixRe-split the group’s sizes from UTC today on: source (actual, prelaunch, explicit), mix (explicit: item id → share), allLocations, keepEdited, dryRun. Returns asOf, applied[], skipped[], previousMix[]
POST /new-product-plans/{id}/rebuildRecalculate every curve from the saved similar products, shares, volume and launch dates; hand-edited curves are replaced. Returns rebuilt (row count)
POST /new-product-plans/{id}/purchase-ordersCreate linked draft purchase orders: coverageDays, coverFrom, safetyFactor, moq, packSize, minOnePerSize, onHand, incoming, supplierId, poName, splitByLocation, locationId, locationIds. Returns purchaseOrders[], failed[], linkFailures[]; can be a partial 200
POST /new-product-plans/{id}/locationsAdd locationId: copies the plan’s items and size groups there, built from that location’s history. 201. Returns locationId, itemsCreated, groupsCreated, warnings, historyWarnings
DELETE /new-product-plans/{id}/locations/{locationId}Remove a location’s items and groups. The last location can’t be removed (LAST_LOCATION). Returns locationId, itemsRemoved, groupsRemoved, convertedFromAllLocations
POST /new-product-plans/{id}/locations/syncFor a plan created with locationIds: "all": add active locations it doesn’t have yet. Returns added[], skippedLocations, skipped
  • Items: each item is a variantId with its own similarProducts (up to 10: variantId, optional weight, historyFrom, historyStartDate) and optional volume, launchDate, explicit curve, or sizeGroupKey + sharePercent to make it a size of a sizeGroups[] entry. volume is a multiplier on the similar products’ sales (1 = the same); the header volume is the default for items without their own.
  • Locations: with locationIds, every item gets one row per location, each built from that location’s own history. An item’s own locationId is then ignored (a warning says so).
  • Curves: an explicit curve holds 1–180 daily units starting on the row’s launch date, rounded to 2 decimals. A size takes a curve or a sharePercent in one call, not both (SIZE_CURVE_AND_SHARE).
  • Forecast: a plan adds launch demand only when the header feedsForecast is true (off by default, editable in Draft and Confirmed), includedInForecast is true (on by default, switchable via /forecast-feed even while Locked) and the plan is Confirmed or Locked.
  • After launch: generated size curves, share and volume changes, rebuilds and mix changes keep the days before UTC today as they are. An explicit curve and a moved launch date are applied as sent. apply-mix with source: "actual" needs 14 days and 30 units of sales (MIX_GATE_NOT_MET); previousMix holds the old shares so you can send them back as an explicit mix.
  • Statuses: header, item, size-group, mix and rebuild changes work in Draft and Confirmed. Purchase orders can be created in Draft, Confirmed and Locked.
  • Ordering: a plan with locations gets one order per location by default; splitByLocation: false creates one combined order received at locationId, applying MOQ, pack size and rounding once to each variant’s total (once per size family), so it can order less than the per-location orders together. coverFrom: "today" covers from today’s curve day and subtracts each line’s stock at its location; onHand / incoming replace that stock for the whole plan.
  • Order-quantity preview: each line carries suggestedQuantity; with coverFrom=today also coverFrom, coverageFromDay, stockOnHand and stockIncoming. The preview takes no stock override.

Unit and revenue targets per location. Sales plans never add demand to the forecast, so they have no forecast-feed and no order routes. List date filter: targetDateFrom / targetDateTo (the window end).

Method + pathDoes
GET /sales-plansList
GET /sales-plans/{id}Header with counts, startDate, endDate, allLocations, locations[]
GET /sales-plans/{id}/itemsItem page
GET /sales-plans/{id}/plan-vs-actualDaily plan against actual units and revenue: asOf, currencyCode, aggregate, items[], offPlan[] (not with locationId), warnings. Optional locationId; collapse=variant adds cells[] and perLocation[]
POST /sales-plansCreate a Draft: locationIds (or "all"), startDate, endDate; optional name, taxonomy, notes, seed (variantIds, growthFactor, baselineStart). 201 + Location. Returns inventoryPlanId, planNumber, locationIds, itemsCreated, warnings, historyWarnings
PATCH /sales-plans/{id}Edit name, taxonomy, notes, startDate, endDate, reseedTargets. Returns inventoryPlan, plus reseeded, warnings, historyWarnings when targets were re-seeded
POST /sales-plans/{id}/statusChange status
DELETE /sales-plans/{id}Delete the plan
POST /sales-plans/{id}/itemsAdd variantIds (1–500) with an optional growthFactor, seeded at every plan location from last year. 201. Returns addedCount, skipped, warnings, historyWarnings
PATCH /sales-plans/{id}/items/{inventoryPlanItemId}Edit one item: plannedUnits, notes, curve, revenueCurve. Returns item, currencyCode
DELETE /sales-plans/{id}/items?itemIds=a,bBulk item delete
POST /sales-plans/{id}/growthSet growthFactor (0–10), optionally only for variantIds and / or one locationId. Returns updated, reseeded, warnings, historyWarnings
POST /sales-plans/{id}/locationsAdd locationId, seeded from that location’s last year with the saved growth factors. 201. Returns locationId, itemsCreated, warnings, historyWarnings
DELETE /sales-plans/{id}/locations/{locationId}Remove a location and its rows. The last location can’t be removed. Returns locationId, itemsRemoved, convertedFromAllLocations
POST /sales-plans/{id}/locations/syncFor a plan created with locationIds: "all": add active locations it doesn’t have yet. Returns added[], skippedLocations, skipped
  • Seeding: each row starts from its own location’s sales of the same days last year × growthFactor (1 = unchanged, 1.2 = +20%). A location never borrows another location’s history; a location without history seeds zeros and is named in historyWarnings.
  • Unknown variants refuse the whole batch (UNKNOWN_VARIANTS, with variantIds).
  • Growth: /growth rescales each matching row’s current curve, so hand edits keep their proportions. A row at factor 0, or whose curve sums to 0, is re-seeded from history instead (hand edits on it are replaced).
  • Window: changing startDate / endDate while the plan has rows needs reseedTargets: true, which re-seeds every row and replaces hand edits and revenue overrides. The window can’t be cleared.
  • Item edits: a curve holds one value per day of the plan window. revenueCurve: null restores revenue as units × price. Changing an item’s location, dates or probability is refused (SALES_PLAN_LOCATION_FIXED, SALES_PLAN_WINDOW_FIXED, SALES_PLAN_FIELD_FIXED).

Holidays and sales events that lift the forecast once switched on. Create your own event (name, category, optional dateRule, durationDays, occurrences) or one from the catalogue (catalogueHolidayId plus optional name, durationDays, uplift and notes; the catalogue supplies the category and date rule, so category, dateRule and occurrences are refused).

Method + pathDoes
GET /promotional-eventsList: events[] (each with nextOccurrence), totalCount, cursor. Filters: search, categories (comma-separated), limit (up to 50, default 20), cursor
GET /promotional-events/{id}event, stored occurrences[], rule-based projectedOccurrences[], nextOccurrence, the window they cover and today
POST /promotional-eventsCreate an event, switched off. 201 + Location. Returns the event detail
PATCH /promotional-events/{id}Edit settings, archived or feedsForecast. Returns the event detail
DELETE /promotional-events/{id}Delete the event. Returns deleted, eventId
POST /promotional-events/{id}/occurrencesStore one year’s dates: startDate, endDate (both required), optional uplift, isSkipped. 201. Returns occurrence
PATCH /promotional-events/{id}/occurrences/{occurrenceId}Edit only the fields you send; omitted dates keep their stored values. Returns occurrence
DELETE /promotional-events/{id}/occurrences/{occurrenceId}Delete a stored occurrence. Returns deleted, occurrenceId
POST /promotional-events/{id}/occurrences/{occurrenceId}/analyzeMeasure an ended occurrence’s uplift against the days before it and save the result as the event’s measured uplift (optional baselineDays 7–365, saveBaselineDays). Returns analysis, occurrence, materialized, event
POST /promotional-events/{id}/analyzeThe same for a startDate / endDate window, storing it as an occurrence. Returns analysis, occurrence, materialized, event
POST /promotional-events/{id}/analyze-past-yearsMeasure up to three past occurrences, newest first (optional baselineDays). Returns years[], range, applies, event
  • Switched off at first: every new event has feedsForecast: false. Turn it on with PATCH /promotional-events/{id} and { "feedsForecast": true }. archived is a separate switch. A forecast view can opt out of event effects on its own.
  • Uplifts are fractions from 0 to 10: 0.35 means +35%. defaultUplift applies to every year; an occurrence’s uplift overrides it for that year (null falls back to the event’s).
  • Ids are created for you: eventId and occurrenceId in a create body are refused.
  • Analysis: results leave out the daily chart series. In analyze-past-years, a year that couldn’t be measured carries error (code, message) inside a 200; send the same request with the same Idempotency-Key to get that answer again without re-measuring the other years.
Method + pathDoes
GET /promotional-event-catalogueSearch the holiday catalogue by name, alias or region code (query; empty lists everything). Returns hits[], each with entry, group and matchedBy

Each entry carries its catalogueHolidayId, name, category, durationDays, aliases, regions and dateRule. Send the catalogueHolidayId to POST /promotional-events.

The demand types, reasons and sources plans are tagged with.

Method + pathDoes
GET /inventory-plan-optionsoptions[], optionally only one type (demandType, reasonCode, source)
POST /inventory-plan-optionsCreate: type, name, optional description, color, sortOrder. 201. Returns option
PATCH /inventory-plan-options/{optionId}Edit name, description, color, sortOrder or active (not isActive); at least one. Returns option
DELETE /inventory-plan-options/{optionId}Delete the option. Returns deleted, optionId

A name already used within the same type is refused with 400 and error.data.code: "DUPLICATE_NAME", carrying the existing optionId. Plans store the option’s name in demandType, reasonCode or source, so renaming or deleting an option doesn’t change existing plans.

Bodies

  • Request bodies are strict and belong to one collection: a field from another plan type, an unknown field, a string where a number or boolean is expected, or a system-owned field (planType, eventId, occurrenceId) returns 400.
  • Ids in the path win. A matching id in the body is accepted; a different one returns 400.
  • Bulk item delete reads only the path and the itemIds query.
  • Creates return the ids, numbers, counts and warnings the write produced, not the full resource. Read the plan or its items when you need the current state.

Dates and units

  • Every planning date is a YYYY-MM-DD UTC day, including curve days, analytics, event windows and “today” after a launch. Timestamps such as createdAt are UTC instants.
  • Prices and revenue are in the shop currency, given by currencyCode on the response.
  • Share fields (sharePercent, prelaunchSharePercent) run from 0 to 100. attainmentPercent and revenueAttainmentPercent are actual ÷ planned × 100: above 100 when sales beat the plan, null when nothing is planned yet. probability is a fraction from 0 to 1. Event uplifts are fractions (0.35 = +35%). growthFactor and volume are multipliers (1.2 = +20%).
  • coverageFromDay is a zero-based day index into the curve, not a date.
  • Query booleans (includeCurves, includeFamily, minOnePerSize) are on only for the literal true.

Idempotency and partial results

  • Send an Idempotency-Key on every create and every order action.
  • An order action that created some orders answers 200: keep the created ids, then read failed and linkFailures (linkFailure for a transfer order). Replay the same key to get that answer again; retry only the failed supplierIds or locationIds with a new key. Planning create and order retries has the full rules.

Not exposed over REST

  • Previews and calculators that don’t save anything (curve generation, mix previews, history lookups, sales-plan seed previews).
  • Plan documents, PDFs, exports and the activity log; restoring products at a location.
  • Creating a Shopify draft (sales) order from a commitment.
  • A separate single-item delete or curve route for new product plans: use the bulk item delete and the item PATCH with curve.
  • Plan-vs-actual for commitments, and forecast-feed or order routes for sales plans.