openapi: 3.0.1 info: title: Happy Buyers External API description: 'External API for Happy Buyers organization metadata and inventory data. Requests are authenticated with an API key in the hca-api-key header. Results are scoped to the organization attached to that key. List endpoints use limit/offset pagination and return totalCount plus hasMore. ' version: v1 servers: - url: https://api.happycabbage.ai description: External API security: - ApiKeyAuth: [] tags: - name: POS Brands description: Organization POS brand metadata and mappings - name: Orders description: Orders - name: Inventory Health description: Inventory health data - name: Identity description: API key identity and organization context - name: Universal Brands description: Canonical brand metadata across POS systems - name: Daily Sales Metadata description: Daily product sales and stock metadata - name: Packages description: Package-level product inventory - name: Stores description: Organization location metadata - name: Product Inventory description: Product-level inventory, sales, and demand metrics - name: Product Line Inventory description: Product-line inventory, demand, and replenishment metrics - name: Product Lines description: Product-lines - name: POS Categories description: Organization POS category metadata and mappings - name: Blockout Dates description: Blockout dates - name: Universal Categories description: Canonical product category metadata across POS systems paths: /external/v1/product-lines/{id}: get: tags: - Product Lines summary: Get a product line description: Returns one product line for the API key organization. Requires `product_lines:read`. operationId: getProductLine parameters: - name: id in: path required: true schema: type: integer format: int32 responses: '200': description: Product line details content: application/json: schema: $ref: '#/components/schemas/ProductLineDetailResponse' '401': description: Missing, malformed, or invalid API key '404': description: Product line not found '403': description: API key does not include product_lines:read put: tags: - Product Lines summary: Update a product line description: Updates a product line for the API key organization. Requires `product_lines:write`. operationId: updateProductLine parameters: - name: id in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/ProductLineUpdateRequest' required: true responses: '400': description: Invalid request body '401': description: Missing, malformed, or invalid API key '404': description: Product line not found '403': description: API key does not include product_lines:write '200': description: Updated product line content: application/json: schema: $ref: '#/components/schemas/ProductLineDetailResponse' /external/v1/product-inventory/{productId}/stores/{storeId}/carry-status: put: tags: - Product Inventory summary: Update product carry status description: 'Updates whether a product should continue to be carried at a store. Discontinued products will still be viewable but will not be recommended for reorders. Requires `inventory:write`. ' operationId: updateCarryStatus parameters: - name: productId in: path required: true schema: type: integer format: int32 - name: storeId in: path required: true schema: type: string format: uuid requestBody: content: application/json: schema: $ref: '#/components/schemas/CarryStatusUpdateRequest' required: true responses: '400': description: Invalid request body '401': description: Missing, malformed, or invalid API key '404': description: Store or product inventory not found '200': description: Updated carry status content: application/json: schema: $ref: '#/components/schemas/CarryStatusUpdateResponse' '403': description: API key does not include inventory:write /external/v1/orders/{orderId}/items: get: tags: - Orders summary: List order items description: Returns product and product-line items for an order in the API key organization. Requires `orders:read`. operationId: getOrderItems parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: limit in: query description: Maximum records to return. Defaults to `100`. required: false schema: maximum: 500 minimum: 0 type: string description: Maximum records to return. Defaults to `100`. default: '100' - name: offset in: query description: Zero-based record offset. Defaults to `0`. required: false schema: minimum: 0 type: string description: Zero-based record offset. Defaults to `0`. default: '0' - name: sortTarget in: query description: Field used to sort order items. required: false schema: type: string description: Field used to sort order items. - name: sortDirection in: query description: Sort direction. Defaults to `ASC`. required: false schema: type: string description: Sort direction. Defaults to `ASC`. default: ASC responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseOrderItemResponse' put: tags: - Orders summary: Add order item description: Adds a product, product-line, or custom item to an order. Requires `orders:write`. operationId: addOrderItem parameters: - name: orderId in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderItemAddRequest' required: true responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OrderItemResponse' /external/v1/orders/{orderId}/invoices/{invoiceId}: get: tags: - Orders summary: Get order invoice description: Returns invoice metadata for an order in the API key organization. Requires `orders:read`. operationId: getOrderInvoice parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: invoiceId in: path required: true schema: type: integer format: int32 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceResponse' put: tags: - Orders summary: Update order invoice description: Updates invoice metadata for an order in the API key organization. Requires `orders:write`. operationId: updateOrderInvoice parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: invoiceId in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceUpdateRequest' required: true responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceResponse' delete: tags: - Orders summary: Remove order invoice description: Removes invoice metadata and its file from an order in the API key organization. Requires `orders:write`. operationId: removeOrderInvoice parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: invoiceId in: path required: true schema: type: integer format: int32 responses: '200': description: OK content: application/json: schema: type: object /external/v1/orders/{id}: put: tags: - Orders summary: Update an order description: Updates order metadata for the API key organization. Requires `orders:write`. operationId: updateOrder parameters: - name: id in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateRequest' required: true responses: '404': description: Order not found '400': description: Invalid request body '401': description: Missing, malformed, or invalid API key '403': description: API key does not include orders:write '200': description: Updated order content: application/json: schema: $ref: '#/components/schemas/OrderResponse' /external/v1/blockout-dates/{id}: get: tags: - Blockout Dates summary: Get a blockout date description: Returns one blockout date. Requires `blockout_dates:read`. operationId: getBlockoutDate parameters: - name: id in: path required: true schema: type: integer format: int32 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/BlockoutDateResponse' put: tags: - Blockout Dates summary: Update a blockout date description: Updates a blockout date. Requires `blockout_dates:write`. operationId: updateBlockoutDate parameters: - name: id in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockoutDateRequest' required: true responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/BlockoutDateResponse' delete: tags: - Blockout Dates summary: Delete a blockout date description: Deletes a blockout date. Requires `blockout_dates:write`. operationId: deleteBlockoutDate parameters: - name: id in: path required: true schema: type: integer format: int32 responses: '200': description: OK content: application/json: schema: type: object /external/v1/product-lines: get: tags: - Product Lines summary: List product lines description: Returns product lines for the API key organization. Requires `product_lines:read`. operationId: getProductLines parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of product lines to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: isFavorite in: query description: Filter by whether the product line is marked as a favorite. required: false schema: type: boolean example: true - name: universalCategoryIds in: query description: Universal category ID filters. Pass repeated query parameters, for example universalCategoryIds=1&universalCategoryIds=2. required: false schema: type: array items: type: integer format: int32 example: 12 - name: posBrandIds in: query description: POS brand ID filters. Pass repeated query parameters, for example posBrandIds=1&posBrandIds=2. required: false schema: type: array items: type: integer format: int32 example: 42 - name: search in: query description: Case-insensitive product line search text. required: false schema: type: string example: gummies - name: productLineId in: query description: Exact product line ID filter. required: false schema: type: integer format: int32 example: 123 - name: universalBrandIds in: query description: Universal brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 90 responses: '401': description: Missing, malformed, or invalid API key '200': description: Paged product line results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseProductLineDetailResponse' '400': description: Invalid query parameter '403': description: API key does not include product_lines:read post: tags: - Product Lines summary: Create a product line description: Creates a new product line for the API key organization. Requires `product_lines:write`. operationId: createProductLine requestBody: content: application/json: schema: $ref: '#/components/schemas/ProductLineCreateRequest' required: true responses: '200': description: Created product line content: application/json: schema: $ref: '#/components/schemas/ProductLineDetailResponse' '400': description: Invalid request body '401': description: Missing, malformed, or invalid API key '403': description: API key does not include product_lines:write /external/v1/orders: get: tags: - Orders summary: List orders description: Returns `DRAFT`, `READY_TO_SEND`, `SUBMITTED`, and `RECEIVED` orders for the API key organization. Requires `orders:read`. operationId: getOrders parameters: - name: limit in: query description: Maximum records to return. Defaults to `100`. required: false schema: maximum: 500 minimum: 0 type: string description: Maximum records to return. Defaults to `100`. default: '100' - name: offset in: query description: Zero-based record offset. Defaults to `0`. required: false schema: minimum: 0 type: string description: Zero-based record offset. Defaults to `0`. default: '0' - name: sortTarget in: query description: Field used to sort orders. `CREATED_AT` sorts by order creation time. required: false schema: type: string description: Field used to sort orders. `CREATED_AT` sorts by order creation time. - name: sortDirection in: query description: Sort direction. Defaults to `DESC`. required: false schema: type: string description: Sort direction. Defaults to `DESC`. default: DESC - name: universalBrandId in: query description: Only return orders containing products for this universal brand id. required: false schema: type: string description: Only return orders containing products for this universal brand id. - name: status in: query description: Only return orders with this status, including `DRAFT`. required: false schema: type: string description: Only return orders with this status, including `DRAFT`. - name: createdAtFrom in: query description: Minimum inclusive order `createdAt` timestamp. required: false schema: type: string description: Minimum inclusive order `createdAt` timestamp. - name: createdAtUntil in: query description: Maximum exclusive order `createdAt` timestamp. required: false schema: type: string description: Maximum exclusive order `createdAt` timestamp. - name: includeArchived in: query description: Include archived orders. Defaults to `false`. required: false schema: type: string description: Include archived orders. Defaults to `false`. default: 'false' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseOrderResponse' post: tags: - Orders summary: Create an order description: Creates a `DRAFT` order for the API key organization. Requires `orders:write`. operationId: createOrder requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateRequest' required: true responses: '200': description: Created order content: application/json: schema: $ref: '#/components/schemas/OrderResponse' '400': description: Invalid request body '401': description: Missing, malformed, or invalid API key '403': description: API key does not include orders:write /external/v1/orders/{orderId}/invoices: get: tags: - Orders summary: List order invoices description: Returns invoice metadata for an order in the API key organization. Requires `orders:read`. operationId: getOrderInvoices parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: limit in: query description: Maximum records to return. Defaults to `100`. required: false schema: maximum: 500 minimum: 0 type: string description: Maximum records to return. Defaults to `100`. default: '100' - name: offset in: query description: Zero-based record offset. Defaults to `0`. required: false schema: minimum: 0 type: string description: Zero-based record offset. Defaults to `0`. default: '0' - name: sortTarget in: query description: Field used to sort invoices. required: false schema: type: string description: Field used to sort invoices. - name: sortDirection in: query description: Sort direction. Defaults to `DESC`. required: false schema: type: string description: Sort direction. Defaults to `DESC`. default: DESC - name: search in: query description: Search text matched against invoice name and filename. required: false schema: type: string description: Search text matched against invoice name and filename. responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseOrderInvoiceResponse' post: tags: - Orders summary: Add order invoice description: Adds invoice metadata to an order in the API key organization. Requires `orders:write`. operationId: addOrderInvoice parameters: - name: orderId in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceCreateRequest' required: true responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceResponse' /external/v1/orders/{orderId}/invoices/{invoiceId}/upload-url: post: tags: - Orders summary: Generate order invoice upload URL description: Generates a temporary upload URL for an order invoice in the API key organization. Requires `orders:write`. operationId: generateTemporaryUploadUrl parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: invoiceId in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceUrlRequest' required: true responses: '200': description: OK content: application/json: schema: type: string /external/v1/orders/{orderId}/invoices/{invoiceId}/download-url: post: tags: - Orders summary: Generate order invoice download URL description: Generates a temporary download URL for an order invoice in the API key organization. Requires `orders:read`. operationId: generateTemporaryDownloadUrl parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: invoiceId in: path required: true schema: type: integer format: int32 requestBody: content: application/json: schema: $ref: '#/components/schemas/OrderInvoiceUrlRequest' required: true responses: '200': description: OK content: application/json: schema: type: string /external/v1/blockout-dates: get: tags: - Blockout Dates summary: List blockout dates description: List blockout dates. Requires `blockout_dates:read`. operationId: getBlockoutDates parameters: - name: limit in: query description: Maximum records to return. Defaults to 100. required: false schema: maximum: 500 minimum: 0 type: string description: Maximum records to return. Defaults to 100. default: '100' - name: offset in: query description: Zero-based result offset. Defaults to 0. required: false schema: minimum: 0 type: string description: Zero-based result offset. Defaults to 0. default: '0' - name: sortTarget in: query description: Field used to sort results. Defaults to START_DATE. required: false schema: type: string description: Field used to sort results. Defaults to START_DATE. - name: sortDirection in: query description: Sort direction. Defaults to ASC. required: false schema: type: string description: Sort direction. Defaults to ASC. default: ASC - name: storeId in: query description: Only return blockout dates for this store UUID. required: false schema: type: string description: Only return blockout dates for this store UUID. - name: includeAllStores in: query description: When filtering by `storeId`, include blockout dates that apply to all stores when `true`, or exclude them when `false`. required: false schema: type: string description: When filtering by `storeId`, include blockout dates that apply to all stores when `true`, or exclude them when `false`. - name: search in: query description: Case-insensitive blockout date name or description search text. required: false schema: type: string description: Case-insensitive blockout date name or description search text. - name: isAllStores in: query description: Only return blockout dates that apply to all stores. required: false schema: type: string description: Only return blockout dates that apply to all stores. - name: isRecurring in: query description: Only return recurring or non-recurring blockout dates. required: false schema: type: string description: Only return recurring or non-recurring blockout dates. - name: activeOnOrAfter in: query description: Only return blockout dates active on or after this date, inclusive. required: false schema: type: string description: Only return blockout dates active on or after this date, inclusive. - name: activeBefore in: query description: Only return blockout dates active before this date, exclusive. required: false schema: type: string description: Only return blockout dates active before this date, exclusive. responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseBlockoutDateResponse' post: tags: - Blockout Dates summary: Create a blockout date description: Creates a blockout date. Requires `blockout_dates:write`. operationId: createBlockoutDate requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockoutDateRequest' required: true responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/BlockoutDateResponse' /external/v1/whoami: get: tags: - Identity summary: Identify the current API key description: Returns the API key name, key creation time, and organization context for the authenticated hca-api-key header. operationId: whoami responses: '200': description: Authenticated API key identity content: application/json: schema: $ref: '#/components/schemas/IdentityResponse' '401': description: Missing, malformed, or invalid API key /external/v1/universal-categories: get: tags: - Universal Categories summary: List universal categories description: Returns canonical product categories. Results are global categories and are available to any authenticated external API key. operationId: getUniversalCategories parameters: - name: limit in: query description: Maximum number of categories to return. Maximum is 100. required: false schema: maximum: 100 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Case-insensitive category name or label search text. required: false schema: type: string example: flower - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - LABEL example: LABEL - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: ASC responses: '401': description: Missing, malformed, or invalid API key '200': description: Paged universal category results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseUniversalCategoryResponse' '400': description: Invalid query parameter /external/v1/universal-brands: get: tags: - Universal Brands summary: List universal brands description: Returns canonical brands for the API key organization. Requires the organization_metadata:read scope. operationId: getUniversalBrands parameters: - name: limit in: query description: Maximum number of brands to return. Maximum is 100. required: false schema: maximum: 100 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Case-insensitive brand name search text. required: false schema: type: string example: acme - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - NAME example: NAME - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: ASC responses: '403': description: API key does not include organization_metadata:read '200': description: Paged universal brand results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseUniversalBrandResponse' '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/stores: get: tags: - Stores summary: List stores description: Returns stores and locations for the API key organization. Distribution fulfillment relationships are included when configured. Requires organization_metadata:read. operationId: getStores parameters: - name: limit in: query description: Maximum number of stores to return. Maximum is 100. required: false schema: maximum: 100 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Case-insensitive store/location name search text. required: false schema: type: string example: downtown - name: type in: query description: Store/location type filter. required: false schema: type: string enum: - RETAIL - DISTRIBUTION_CENTER - CULTIVATION - MANUFACTURING example: RETAIL - name: fulfilledByLocationId in: query description: Distribution center UUID used to filter locations fulfilled by that location. required: false schema: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 responses: '403': description: API key does not include organization_metadata:read '401': description: Missing, malformed, or invalid API key '200': description: Paged store results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseStoreResponse' '400': description: Invalid query parameter /external/v1/product-lines/{id}/product-inventory: get: tags: - Product Lines summary: Find product inventory inside a product line operationId: getProductLineProductInventory parameters: - name: id in: path required: true schema: type: integer format: int32 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of products to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: isInStock in: query description: When true, only products with inventory on hand are returned. required: false schema: type: boolean example: true - name: continueToCarry in: query description: Filter by whether the product should continue to be carried. required: false schema: type: boolean example: true - name: minimumPredictedDaysOnHand in: query description: Minimum predicted days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 3 - name: maximumPredictedDaysOnHand in: query description: Maximum predicted days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 30 - name: minimumPredictedSellableDaysOnHand in: query description: Minimum predicted sellable days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 3 - name: maximumPredictedSellableDaysOnHand in: query description: Maximum predicted sellable days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 30 - name: minimumInventory in: query description: Minimum current inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 1 - name: maximumInventory in: query description: Maximum current inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 100 - name: minimumSellableInventory in: query description: Minimum sellable inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 1 - name: maximumSellableInventory in: query description: Maximum sellable inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 100 - name: minimumAgedInventoryCost in: query description: Minimum cost of inventory aged 90 or more days. required: false schema: minimum: 0 type: number format: double example: 100.0 - name: maximumAgedInventoryCost in: query description: Maximum cost of inventory aged 90 or more days. required: false schema: minimum: 0 type: number format: double example: 1000.0 - name: desiredDaysOnHand in: query description: Target days on hand used for replenishment calculations. required: false schema: minimum: 1 type: integer format: int32 default: 14 example: 14 - name: leadTime in: query description: Lead time in days used for replenishment calculations. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: lookbackPeriodDays in: query description: Sales lookback window in days. Allowed values are 1, 7, 14, 21, 30, 60, and 90. required: false schema: type: integer format: int32 default: 30 enum: - 1 - 7 - 14 - 21 - 30 - 60 - 90 example: 30 - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - LAST_ACTIVITY_AT example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseProductInventoryResponse' /external/v1/product-line-inventory: get: tags: - Product Line Inventory summary: List product line inventory description: Returns demand-group/product-line inventory, sales velocity, aging, and replenishment metrics for the API key organization. Requires inventory:read. operationId: getProductLineInventory parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of product lines to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: restockOnly in: query description: When true, only product lines with units to order are returned. required: false schema: type: boolean example: true - name: lowDepthOnly in: query description: When true, only product lines below desired product depth are returned. required: false schema: type: boolean example: true - name: isFavorite in: query description: Filter by whether the product line is marked as a favorite. required: false schema: type: boolean example: true - name: universalCategoryIds in: query description: Universal category ID filters. Pass repeated query parameters, for example universalCategoryIds=1&universalCategoryIds=2. required: false schema: type: array items: type: integer format: int32 example: 12 - name: posBrandIds in: query description: POS brand ID filters. Pass repeated query parameters, for example posBrandIds=1&posBrandIds=2. required: false schema: type: array items: type: integer format: int32 example: 42 - name: search in: query description: Case-insensitive product line search text. required: false schema: type: string example: gummies - name: productLineId in: query description: Exact product line ID filter. required: false schema: type: integer format: int32 example: 123 - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - REPORT_RUN_AT - AGED_INVENTORY_COST - INVENTORY_COST - DOLLARS_SOLD - ESTIMATED_PROFIT - AVERAGE_DISCOUNT_PERCENT - AVERAGE_MARGIN_PERCENT example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: universalBrandIds in: query description: Universal brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 90 - name: includeProductLineDetails in: query description: Whether or not to include product line details with the inventory response. required: false schema: type: boolean default: false responses: '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '200': description: Paged product line inventory results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseProductLineInventoryResponse' '400': description: Invalid query parameter /external/v1/product-inventory: get: tags: - Product Inventory summary: List product inventory description: Returns product-level inventory, sales velocity, pricing, cost, age, and replenishment metrics for the API key organization. Requires inventory:read. operationId: getProductInventory parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of products to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: isInStock in: query description: When true, only products with inventory on hand are returned. required: false schema: type: boolean example: true - name: continueToCarry in: query description: Filter by whether the product should continue to be carried. required: false schema: type: boolean example: true - name: restockOnly in: query description: When true, only products with units to order are returned. required: false schema: type: boolean example: true - name: activeInLastDays in: query description: Minimum active sales days in the lookback window. required: false schema: minimum: 0 type: integer format: int32 example: 7 - name: matchingSearchTerms in: query description: Product search terms that must all match product names, while any may match SKUs. Pass repeated query parameters, for example matchingSearchTerms=blue&matchingSearchTerms=dream. required: false schema: type: array items: type: string example: blue - name: notMatchingSearchTerms in: query description: Product name search terms to exclude. Pass repeated query parameters, for example notMatchingSearchTerms=sample¬MatchingSearchTerms=test. required: false schema: type: array items: type: string example: sample - name: universalCategoryIds in: query description: Universal category ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 12 - name: posCategoryIds in: query description: POS category ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 34 - name: posSubCategoryIds in: query description: POS subcategory ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 56 - name: vendorIds in: query description: Vendor ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 78 - name: universalBrandIds in: query description: Universal brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 90 - name: posBrandIds in: query description: POS brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 42 - name: includeUniversalUncategorized in: query description: Include products without a mapped universal category. required: false schema: type: boolean example: true - name: includePosUncategorized in: query description: Include products without a POS category. required: false schema: type: boolean example: true - name: includePosSubUncategorized in: query description: Include products without a POS subcategory. required: false schema: type: boolean example: true - name: minWeight in: query description: Minimum product weight. required: false schema: minimum: 0 type: number format: double example: 3.5 - name: maxWeight in: query description: Maximum product weight. required: false schema: minimum: 0 type: number format: double example: 28.0 - name: strainClassificationIds in: query description: Strain classification ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 5 - name: minPrice in: query description: Minimum unit price. required: false schema: minimum: 0 type: number format: double example: 10.0 - name: maxPrice in: query description: Maximum unit price. required: false schema: minimum: 0 type: number format: double example: 50.0 - name: minCost in: query description: Minimum unit cost. required: false schema: minimum: 0 type: number format: double example: 5.0 - name: maxCost in: query description: Maximum unit cost. required: false schema: minimum: 0 type: number format: double example: 30.0 - name: minimumPredictedDaysOnHand in: query description: Minimum predicted days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 3 - name: maximumPredictedDaysOnHand in: query description: Maximum predicted days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 30 - name: minimumPredictedSellableDaysOnHand in: query description: Minimum predicted sellable days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 3 - name: maximumPredictedSellableDaysOnHand in: query description: Maximum predicted sellable days on hand. required: false schema: minimum: 0 type: integer format: int32 example: 30 - name: minimumInventory in: query description: Minimum current inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 1 - name: maximumInventory in: query description: Maximum current inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 100 - name: minimumSellableInventory in: query description: Minimum sellable inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 1 - name: maximumSellableInventory in: query description: Maximum sellable inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 100 - name: minimumAgedInventoryCost in: query description: Minimum cost of inventory aged 90 or more days. required: false schema: minimum: 0 type: number format: double example: 100.0 - name: maximumAgedInventoryCost in: query description: Maximum cost of inventory aged 90 or more days. required: false schema: minimum: 0 type: number format: double example: 1000.0 - name: desiredDaysOnHand in: query description: Target days on hand used for replenishment calculations. required: false schema: minimum: 1 type: integer format: int32 default: 14 example: 14 - name: leadTime in: query description: Lead time in days used for replenishment calculations. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: lookbackPeriodDays in: query description: Sales lookback window in days. Allowed values are 1, 7, 14, 21, 30, 60, and 90. required: false schema: type: integer format: int32 default: 30 enum: - 1 - 7 - 14 - 21 - 30 - 60 - 90 example: 30 - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - LAST_ACTIVITY_AT example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC responses: '200': description: Paged product inventory results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseProductInventoryResponse' '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/product-inventory/packages: get: tags: - Packages summary: List packages description: Returns package-level product inventory for the API key organization. Requires inventory:read. operationId: findPackages parameters: - name: limit in: query description: Maximum number of packages to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Package search text matched against product name, POS package ID, and regulatory ID. required: false schema: type: string example: blue - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: universalCategoryIds in: query description: Universal category ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 12 - name: posCategoryIds in: query description: POS category ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 34 - name: posSubCategoryIds in: query description: POS subcategory ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 56 - name: universalBrandIds in: query description: Universal brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 90 - name: posBrandIds in: query description: POS brand ID filters, passed as repeated query parameters. required: false schema: type: array items: type: integer format: int32 example: 42 - name: hasUniversalCategory in: query description: Filter by mapped universal category presence. Use false to return packages without a mapped universal category. required: false schema: type: boolean example: true - name: hasPosCategory in: query description: Filter by POS category presence. Use false to return packages without a POS category. required: false schema: type: boolean example: true - name: hasPosSubCategory in: query description: Filter by POS subcategory presence. Use false to return packages without a POS subcategory. required: false schema: type: boolean example: true - name: minDaysAged in: query description: Minimum package age in days. required: false schema: minimum: 0 type: integer format: int32 example: 30 - name: maxDaysAged in: query description: Maximum package age in days. required: false schema: minimum: 0 type: integer format: int32 example: 90 - name: receivedAtStart in: query description: Earliest package received timestamp, inclusive. required: false schema: type: string format: date-time - name: receivedAtEnd in: query description: Latest package received timestamp, exclusive. required: false schema: type: string format: date-time - name: minCurrentInventory in: query description: Minimum current package inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 1 - name: maxCurrentInventory in: query description: Maximum current package inventory units. required: false schema: minimum: 0 type: integer format: int32 example: 100 - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - DAYS_AGED - RECEIVED_AT - CURRENT_INVENTORY - LAST_ACTIVITY_AT example: LAST_ACTIVITY_AT - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: isInStock in: query description: When true, only packages with inventory on hand are returned. required: false schema: type: boolean example: true responses: '200': description: Paged package inventory results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponsePackageResponse' '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/pos-categories: get: tags: - POS Categories summary: List POS categories description: Returns POS-specific categories and subcategories for the API key organization, including mapped universal category information when available. Requires organization_metadata:read. operationId: getPosCategories parameters: - name: limit in: query description: Maximum number of POS categories to return. Maximum is 100. required: false schema: maximum: 100 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Case-insensitive POS category name or label search text. required: false schema: type: string example: edible - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - LABEL - CREATED_AT example: LABEL - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: ASC - name: isSubCategory in: query description: When true, only subcategories are returned; when false, only top-level categories are returned. required: false schema: type: boolean example: false responses: '403': description: API key does not include organization_metadata:read '200': description: Paged POS category results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponsePosCategoryResponse' '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/pos-brands: get: tags: - POS Brands summary: List POS brands description: Returns POS-specific brands for the API key organization, including mapped universal brand information when available. Requires organization_metadata:read. operationId: getPosBrands parameters: - name: limit in: query description: Maximum number of POS brands to return. Maximum is 100. required: false schema: maximum: 100 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: search in: query description: Case-insensitive POS brand name search text. required: false schema: type: string example: acme responses: '403': description: API key does not include organization_metadata:read '401': description: Missing, malformed, or invalid API key '200': description: Paged POS brand results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponsePosBrandResponse' '400': description: Invalid query parameter /external/v1/inventory-health/stores: get: tags: - Inventory Health summary: List store inventory health description: Returns store-level inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getStoreInventoryHealths parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: brandIds in: query description: POS brand ID filters. Pass repeated query parameters, for example brandIds=1&brandIds=2. required: false schema: type: array items: type: integer format: int32 example: 99 - name: categoryIds in: query description: POS category ID filters. Pass repeated query parameters, for example categoryIds=1&categoryIds=2. required: false schema: type: array items: type: integer format: int32 example: 42 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: includePercentShares in: query description: Whether to include percent-share fields. required: false schema: type: boolean example: false - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - UPDATED_AT - PERCENT_SHARE_OF_PROFIT - PERCENT_SHARE_OF_AGED_INVENTORY_COST - PERCENT_SHARE_OF_DOLLARS_SOLD - PERCENT_SHARE_OF_UNITS_SOLD example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC responses: '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '200': description: Paged store inventory health results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseStoreResponse' '400': description: Invalid query parameter /external/v1/inventory-health/stores/history: get: tags: - Inventory Health summary: List store inventory health history description: Returns historical store-level inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getStoreInventoryHealthHistories parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - START_DATE - UNITS_PER_DAY - UPDATED_AT example: START_DATE - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: periodType in: query description: Inventory history period granularity. required: true schema: type: string enum: - WEEKLY - MONTHLY - name: minStartDate in: query description: Minimum inclusive history start date. required: false schema: type: string format: date - name: maxStartDate in: query description: Maximum inclusive history start date. required: false schema: type: string format: date responses: '200': description: Paged store inventory health history results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseStoreHistoryResponse' '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/inventory-health/pos-brands: get: tags: - Inventory Health summary: List POS brand inventory health description: Returns POS brand inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getPosBrandInventoryHealths parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: brandIds in: query description: POS brand ID filters. Pass repeated query parameters, for example brandIds=1&brandIds=2. required: false schema: type: array items: type: integer format: int32 example: 99 - name: categoryIds in: query description: POS category ID filters. Pass repeated query parameters, for example categoryIds=1&categoryIds=2. required: false schema: type: array items: type: integer format: int32 example: 42 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: includePercentShares in: query description: Whether to include percent-share fields. required: false schema: type: boolean example: false - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - UPDATED_AT - PERCENT_SHARE_OF_PROFIT - PERCENT_SHARE_OF_AGED_INVENTORY_COST - PERCENT_SHARE_OF_DOLLARS_SOLD - PERCENT_SHARE_OF_UNITS_SOLD example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: posBrandIds in: query description: Optional target posBrands to filter responses required: false schema: type: array items: type: integer format: int32 example: 332 responses: '200': description: Paged POS brand inventory health results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponsePosBrandResponse' '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/inventory-health/pos-brands/history: get: tags: - Inventory Health summary: List POS brand inventory health history description: Returns historical POS brand inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getPosBrandInventoryHealthHistory parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - START_DATE - UNITS_PER_DAY - UPDATED_AT example: START_DATE - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: periodType in: query description: Inventory history period granularity. required: true schema: type: string enum: - WEEKLY - MONTHLY - name: minStartDate in: query description: Minimum inclusive history start date. required: false schema: type: string format: date - name: maxStartDate in: query description: Maximum inclusive history start date. required: false schema: type: string format: date - name: posBrandIds in: query description: Optional target posBrands to filter responses required: false schema: type: array items: type: integer format: int32 example: 332 responses: '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '200': description: Paged POS brand inventory health history results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponsePosBrandHistoryResponse' '400': description: Invalid query parameter /external/v1/inventory-health/categories: get: tags: - Inventory Health summary: List category inventory health description: Returns POS category inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getCategoryInventoryHealths parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: brandIds in: query description: POS brand ID filters. Pass repeated query parameters, for example brandIds=1&brandIds=2. required: false schema: type: array items: type: integer format: int32 example: 99 - name: categoryIds in: query description: POS category ID filters. Pass repeated query parameters, for example categoryIds=1&categoryIds=2. required: false schema: type: array items: type: integer format: int32 example: 42 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: includePercentShares in: query description: Whether to include percent-share fields. required: false schema: type: boolean example: false - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - UNITS_PER_DAY - UPDATED_AT - PERCENT_SHARE_OF_PROFIT - PERCENT_SHARE_OF_AGED_INVENTORY_COST - PERCENT_SHARE_OF_DOLLARS_SOLD - PERCENT_SHARE_OF_UNITS_SOLD example: UNITS_PER_DAY - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC responses: '200': description: Paged category inventory health results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseCategoryResponse' '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '400': description: Invalid query parameter /external/v1/inventory-health/categories/history: get: tags: - Inventory Health summary: List category inventory health history description: Returns historical POS category inventory health metrics for the API key organization. Results are always separated by store. Requires inventory:read. operationId: getCategoryInventoryHealthHistories parameters: - name: offset in: query description: Zero-based result offset for pagination. required: false schema: minimum: 0 type: integer format: int32 default: 0 example: 0 - name: limit in: query description: Maximum number of inventory health rows to return. Maximum is 500. required: false schema: maximum: 500 minimum: 0 type: integer format: int32 default: 100 example: 100 - name: storeIds in: query description: Store UUID filters. Pass repeated query parameters, for example storeIds=id1&storeIds=id2. required: false schema: type: array items: type: string format: uuid example: 11111111-1111-1111-1111-111111111111 - name: search in: query description: Search text matched against the fields returned by the route. required: false schema: type: string - name: sortTarget in: query description: Field to sort by. required: false schema: type: string enum: - START_DATE - UNITS_PER_DAY - UPDATED_AT example: START_DATE - name: sortDirection in: query description: Sort direction. Prefer ASC or DESC; lowercase values are deprecated aliases. required: false schema: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST example: DESC - name: periodType in: query description: Inventory history period granularity. required: true schema: type: string enum: - WEEKLY - MONTHLY - name: minStartDate in: query description: Minimum inclusive history start date. required: false schema: type: string format: date - name: maxStartDate in: query description: Maximum inclusive history start date. required: false schema: type: string format: date - name: categoryIds in: query description: Optional target categories to filter responses required: false schema: type: array items: type: integer format: int32 example: 1 responses: '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '200': description: Paged category inventory health history results content: application/json: schema: $ref: '#/components/schemas/LimitOffsetResponseCategoryHistoryResponse' '400': description: Invalid query parameter /external/v1/daily-sales-metadata/stores/{storeId}/products/{productId}: get: tags: - Daily Sales Metadata summary: List daily sales metadata for a product description: Returns the last 90 days of daily sales metadata for one product and store in the API key organization. Date boundaries are calculated in the organization's configured timezone and today is excluded. Requires inventory:read. operationId: getDailySalesMetadataForProduct parameters: - name: storeId in: path description: Store/location UUID. required: true schema: type: string format: uuid example: 1ff7efc9-e196-43fb-8638-f5399f137ba6 - name: productId in: path description: Internal product ID returned as productDetail.id from product inventory. required: true schema: type: integer format: int32 example: 12345 responses: '403': description: API key does not include inventory:read '401': description: Missing, malformed, or invalid API key '200': description: Daily sales metadata results content: application/json: schema: type: array items: $ref: '#/components/schemas/DailySalesMetadataResponse' /external/v1/orders/{orderId}/items/{itemId}/store/{storeId}: delete: tags: - Orders summary: Remove order item description: Removes an item from an order. Requires `orders:write`. operationId: removeOrderItem parameters: - name: orderId in: path required: true schema: type: integer format: int32 - name: itemId in: path required: true schema: type: integer format: int64 - name: storeId in: path required: true schema: type: string format: uuid responses: '200': description: OK content: application/json: schema: type: object components: schemas: ProductLineUpdateRequest: required: - name - productQueryParams type: object properties: name: type: string description: Product line/demand group display name. example: Acme Gummies productQueryParams: $ref: '#/components/schemas/ProductQueryParams' isFavorite: type: boolean description: Whether the product line is marked as a favorite. example: true desiredDaysOnHand: minimum: 1 type: integer description: Target days on hand used for product-line replenishment calculations. format: int32 leadTime: minimum: 0 type: integer description: Lead time in days used for product-line replenishment calculations. format: int32 lookbackPeriod: minimum: 1 type: integer description: Sales lookback period in days used for product-line velocity calculations. format: int32 autoRunDays: type: array description: Days of week to auto-run this product line, using ISO day-of-week values. items: type: integer description: Days of week to auto-run this product line, using ISO day-of-week values. format: int32 reportEmailTargetDefaults: type: array description: Default email recipients for product-line reports. items: type: string description: Default email recipients for product-line reports. minimumOrderQuantityDefault: minimum: 0 type: integer description: Default minimum order quantity (case size) for products in this product line. format: int32 targetProductDepth: minimum: 0 type: integer description: Target product depth for this product line. format: int32 autoDemandGroupCreateLogId: type: integer description: Auto-created demand group log ID, when this product line is auto-generated. format: int32 ProductQueryParams: type: object properties: offset: minimum: 0 type: integer format: int32 default: 0 limit: maximum: 500 minimum: 0 type: integer format: int32 default: 100 restockOnly: type: boolean activeInLastDays: minimum: 0 type: integer format: int32 matchingSearchTerms: type: array items: type: string notMatchingSearchTerms: type: array items: type: string universalCategoryIds: type: array items: type: integer format: int32 posCategoryIds: type: array items: type: integer format: int32 posSubCategoryIds: type: array items: type: integer format: int32 vendorIds: type: array items: type: integer format: int32 universalBrandIds: type: array items: type: integer format: int32 posBrandIds: type: array items: type: integer format: int32 includeUniversalUncategorized: type: boolean includePosUncategorized: type: boolean includePosSubUncategorized: type: boolean minWeight: minimum: 0 type: number format: double maxWeight: minimum: 0 type: number format: double strainClassificationIds: type: array items: type: integer format: int32 minPrice: minimum: 0 type: number format: double maxPrice: minimum: 0 type: number format: double minCost: minimum: 0 type: number format: double maxCost: minimum: 0 type: number format: double sortTarget: type: string enum: - UNITS_PER_DAY - LAST_ACTIVITY_AT sortDirection: type: string enum: - asc - desc - ASC - DESC - ASC_NULLS_FIRST - ASC_NULLS_LAST - DESC_NULLS_FIRST - DESC_NULLS_LAST description: The product query params used to find products matching this product line. ProductLineDetailResponse: type: object properties: name: type: string description: Product line/demand group display name. example: Acme Gummies id: type: integer description: Product line/demand group ID. format: int32 example: 123 targetProductDepth: type: integer description: Target product depth for this product line. format: int32 desiredDaysOnHand: type: integer description: Target days on hand used for product-line replenishment calculations. format: int32 leadTime: type: integer description: Lead time in days used for product-line replenishment calculations. format: int32 universalBrandId: type: integer description: Universal brand ID associated with the product line, when available. This is a derived field, set automatically when the product line's query params have one or more brand mapped to the same universal brand or only one universal brand. format: int32 universalBrandName: type: string description: Universal brand name associated with the product line, when available. minimumOrderQuantity: type: integer description: Minimum order quantity (case size) for this product line. format: int32 universalCategoryId: type: integer description: Universal category ID, when available. This is a derived field, set automatically when the product line's query params have only one category. format: int32 universalCategoryLabel: type: string description: Universal category label, when available. posBrandId: type: integer description: POS brand ID associated with the product line, when available. This is a derived field, set automatically when the product line's query params have only one brand. format: int32 posBrandName: type: string description: POS brand name associated with the product line, when available. productQueryParams: $ref: '#/components/schemas/ProductQueryParams' lookbackPeriod: type: integer description: Sales lookback period in days used for product-line velocity calculations. format: int32 CarryStatusUpdateRequest: required: - continueToCarry type: object properties: continueToCarry: type: boolean description: 'Whether this product should continue to be carried. When false: Product is discontinued. It will still be viewable but will not be recommended for reorders. When true: Product will continue to be recommended for reorders. ' example: false CarryStatusUpdateResponse: type: object properties: productId: type: integer description: Internal product ID. format: int32 storeId: type: string description: Store/location UUID. format: uuid continueToCarry: type: boolean description: Whether this product should continue to be carried. OrderItemAddRequest: required: - storeId type: object properties: productId: type: integer description: Product `id` when adding an individual product. format: int32 example: 123 productLineId: type: integer description: Product-line `id` when adding a product-line item. format: int32 example: 456 storeId: type: string description: Store/location `UUID` for the item. format: uuid quantity: minimum: 0 type: integer description: Quantity explicitly placed on the order item. format: int32 example: 10 desiredDaysOnHand: minimum: 0 type: integer description: Target days on hand used for order calculations. format: int32 example: 14 leadTime: minimum: 0 type: integer description: Lead time in days used for order calculations. format: int32 example: 2 lookbackPeriod: minimum: 0 type: integer description: Lookback period in days used for demand calculations. format: int32 example: 30 unitCost: minimum: 0 type: number description: Order item unit cost override. format: double example: 12.5 strainsNeeded: minimum: 0 type: integer description: Strains needed override for product-line items. format: int32 example: 4 note: type: string description: Optional order item note. customName: type: string description: Custom item name, required when adding a custom item. customPosBrandId: type: integer description: '`POS` brand `id` for a custom item.' format: int32 example: 12 customUniversalCategoryId: type: integer description: '`Universal` category `id` for a custom item.' format: int32 example: 34 customCaseSize: minimum: 1 type: integer description: Custom item case size. format: int32 example: 6 targetProductDepth: minimum: 0 type: integer description: Target product depth for product-line items. format: int32 example: 8 description: Request body for adding an item to an order. OrderItemResponse: type: object properties: note: type: string description: Optional order item note. name: type: string description: Product or product-line name. id: type: integer description: Order item `id`. format: int32 predictedDaysOnHand: type: integer description: Predicted days on hand after current inventory and run rate. format: int32 categoryId: type: integer description: '`POS` category `id`.' format: int32 productId: type: integer description: Product `id`. Present when the item is an individual product. format: int32 categoryLabel: type: string description: '`POS` category label.' targetProductDepth: type: integer description: Target product depth for product-line items. format: int32 desiredDaysOnHand: type: integer description: Target days on hand used for order calculations. format: int32 universalBrandId: type: integer description: '`Universal` brand `id`.' format: int32 universalBrandName: type: string description: '`Universal` brand name.' unitsAtDistribution: type: integer description: Units currently available at the configured distribution location. format: int32 unitsNeeded: type: integer description: Calculated units needed for the target days on hand. format: int32 quantity: type: integer description: Current units on hand at the store for this item. format: int32 posBrandId: type: integer description: '`POS` brand `id`.' format: int32 posBrandName: type: string description: '`POS` brand name.' currentProductDepth: type: integer description: Current product depth for product-line items. format: int32 storeName: type: string description: Store/location name. storeId: type: string description: Store/location `UUID`. format: uuid strainsNeeded: type: integer description: Calculated or overridden strains needed for product-line items. format: int32 addedAt: type: string description: Timestamp the item was added to the order. format: date-time itemUnitCost: type: number description: Estimated item unit cost. format: double itemType: type: string description: The type of an item, either a product or a product line. enum: - PRODUCT - PRODUCT_LINE - CUSTOM lookbackPeriod: type: integer description: Lookback period in days used for demand calculations. format: int32 distributionFulfilmentStatus: type: string description: '`DistributionFulfillmentStatus` for the ordered quantity.' enum: - PARTIAL - UNAVAILABLE - AVAILABLE - NO_DISTRIBUTION orderQuantity: type: integer description: Quantity explicitly placed on the order item. format: int32 itemUnitCostCartOverride: type: number description: Order item unit cost override, when set. format: double currentSativaDepth: type: integer description: Current sativa depth for product-line items. format: int32 currentIndicaDepth: type: integer description: Current indica depth for product-line items. format: int32 currentHybridDepth: type: integer description: Current hybrid depth for product-line items. format: int32 productLineId: type: integer description: Product-line `id`. Present when the item is a product line. format: int32 OrderInvoiceUpdateRequest: required: - filename type: object properties: name: type: string description: Optional invoice display name. filename: type: string description: Invoice filename. example: invoice.pdf description: Request body for updating an order invoice. OrderInvoiceResponse: type: object properties: name: type: string description: Optional invoice display name. id: type: integer description: Invoice `id`. format: int32 filename: type: string description: Invoice filename. createdAt: type: string description: Timestamp the invoice metadata was created. format: date-time updatedAt: type: string description: Timestamp the invoice metadata was last updated. format: date-time orderId: type: integer description: Owning order `id`. format: int32 description: Order invoice metadata. UpdateRequest: type: object properties: id: type: integer format: int32 note: type: string name: type: string description: type: string OrderResponse: type: object properties: note: type: string description: Optional order note. name: type: string description: Optional order name. id: type: integer description: Internal order `id`. format: int32 example: 123 status: type: string description: 'Current order status: `DRAFT`, `READY_TO_SEND`, `SUBMITTED`, or `RECEIVED`.' enum: - DRAFT - READY_TO_SEND - SUBMITTED - RECEIVED description: type: string description: Optional order description. createdAt: type: string description: Order creation timestamp. format: date-time orderNumber: type: string description: Organization-facing order number. example: HB-2026-000123 createdByUserId: type: integer description: '`id` of the user who created the order.' format: int32 createdByUserFirstName: type: string description: First name of the user who created the order. createdByUserLastName: type: string description: Last name of the user who created the order. BlockoutDateRequest: required: - endDate - isAllStores - isRecurring - name - posBrandIds - startDate - storeIds - universalCategoryIds type: object properties: name: type: string description: Blockout date display name. example: Thanksgiving description: type: string description: Optional blockout date description. startDate: type: string description: Inclusive first blocked-out date. format: date example: '2026-11-26' endDate: type: string description: Inclusive last blocked-out date. format: date example: '2026-11-27' isRecurring: type: boolean description: Whether the blockout date repeats annually. example: false isAllStores: type: boolean description: Whether this blockout date applies to every store. When false, it applies only to `storeIds`. example: false storeIds: type: array description: Store UUIDs affected when `isAllStores` is false. items: type: string description: Store UUIDs affected when `isAllStores` is false. format: uuid posBrandIds: type: array description: POS brand IDs whose products are blocked out. items: type: integer description: POS brand IDs whose products are blocked out. format: int32 universalCategoryIds: type: array description: Universal category IDs whose products are blocked out. items: type: integer description: Universal category IDs whose products are blocked out. format: int32 description: Request body for creating or updating a blockout date. BlockoutDateResponse: type: object properties: name: type: string description: Blockout date display name. example: Thanksgiving id: type: integer description: Blockout date ID. format: int32 example: 123 description: type: string description: Optional blockout date description. isRecurring: type: boolean description: Whether the blockout date repeats annually. createdAt: type: string description: Blockout date creation timestamp. format: date-time updatedAt: type: string description: Blockout date last-update timestamp. format: date-time startDate: type: string description: Inclusive first blocked-out date. format: date endDate: type: string description: Inclusive last blocked-out date. format: date isAllStores: type: boolean description: Whether the blockout date applies to every store. storeIds: type: array description: Store UUIDs affected when isAllStores is false. items: type: string description: Store UUIDs affected when isAllStores is false. format: uuid posBrandIds: type: array description: POS brand IDs whose products are blocked out. items: type: integer description: POS brand IDs whose products are blocked out. format: int32 universalCategoryIds: type: array description: Universal category IDs whose products are blocked out. items: type: integer description: Universal category IDs whose products are blocked out. format: int32 description: A blockout date for the API key organization. ProductLineCreateRequest: required: - name - productQueryParams type: object properties: name: type: string description: Product line display name. example: Acme Gummies productQueryParams: $ref: '#/components/schemas/ProductQueryParams' isFavorite: type: boolean description: Whether the product line is marked as a favorite. example: true desiredDaysOnHand: minimum: 1 type: integer description: Target days on hand used for product-line replenishment calculations. format: int32 leadTime: minimum: 0 type: integer description: Lead time in days used for product-line replenishment calculations. format: int32 lookbackPeriod: minimum: 1 type: integer description: Sales lookback period in days used for product-line velocity calculations. format: int32 autoRunDays: type: array description: Days of week to auto-run this product line, using ISO day-of-week values. Defaults to Monday through Friday. example: - 1 - 3 - 5 items: type: integer description: Days of week to auto-run this product line, using ISO day-of-week values. Defaults to Monday through Friday. format: int32 default: - 1 - 2 - 3 - 4 - 5 reportEmailTargetDefaults: type: array description: Default email recipients for product-line reports. items: type: string description: Default email recipients for product-line reports. minimumOrderQuantityDefault: minimum: 1 type: integer description: Default minimum order quantity (case size) for products in this product line. format: int32 targetProductDepth: minimum: 0 type: integer description: Target product depth for this product line. format: int32 CreateRequest: type: object properties: note: type: string name: type: string description: type: string OrderInvoiceCreateRequest: required: - filename type: object properties: name: type: string description: Optional invoice display name. filename: type: string description: Invoice filename. example: invoice.pdf description: Request body for adding an invoice to an order. OrderInvoiceUrlRequest: type: object properties: durationSeconds: maximum: 604800 minimum: 1 type: integer description: Temporary URL duration in seconds. Defaults to service configuration. format: int32 description: Request body for generating a temporary invoice file URL. IdentityResponse: type: object properties: keyName: type: string description: Display name of the API key used for the request. example: Partner reporting key organizationName: type: string description: Organization name associated with the API key. example: Happy Buyers keyCreatedAt: type: string description: Timestamp when the API key was created. format: date-time example: '2026-01-01T00:00:00Z' organizationCreatedAt: type: string description: Timestamp when the organization was created, when available. format: date-time LimitOffsetResponseUniversalCategoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/UniversalCategoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true UniversalCategoryResponse: type: object properties: name: type: string description: Canonical category machine name. example: flower id: type: integer description: Canonical category ID. format: int32 example: 12 label: type: string description: Canonical category display label. example: Flower description: Records for the current page. LimitOffsetResponseUniversalBrandResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/UniversalBrandResponse' hasMore: type: boolean description: True when more records are available after this page. example: true UniversalBrandResponse: type: object properties: name: type: string description: Canonical brand name. example: Acme id: type: integer description: Canonical brand ID. format: int32 example: 42 productCount: type: integer description: Number of products currently associated with this brand. format: int32 example: 17 description: Records for the current page. LimitOffsetResponseStoreResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/StoreResponse' hasMore: type: boolean description: True when more records are available after this page. example: true StoreResponse: type: object properties: name: type: string description: Full display name of the store/location. example: Downtown Retail id: type: string description: Store/location UUID. format: uuid example: 11111111-1111-1111-1111-111111111111 type: type: string description: Store/location type. example: RETAIL enum: - RETAIL - DISTRIBUTION_CENTER - CULTIVATION - MANUFACTURING createdAt: type: string description: Timestamp when the store/location was created. format: date-time example: '2026-01-01T00:00:00Z' stateCode: type: string description: Two-letter state code for the store/location. example: CA fulfilledById: type: string description: Distribution center UUID that fulfills this location, when a fulfillment relationship exists. format: uuid fulfilledByStoreName: type: string description: Name of the fulfillment location, when configured. description: Records for the current page. LimitOffsetResponseProductLineDetailResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/ProductLineDetailResponse' hasMore: type: boolean description: True when more records are available after this page. example: true LimitOffsetResponseProductInventoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/ProductInventoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true ProductDetailResponse: type: object properties: name: type: string description: Product display name. example: Acme Gummies 10mg id: type: integer description: Internal product ID. Use this value for product-specific endpoints. format: int32 weight: type: number description: Product weight, when available. format: double example: 3.5 universalBrandId: type: integer description: Mapped universal brand ID, when available. format: int32 universalBrandName: type: string description: Mapped universal brand name, when available. vendorId: type: integer description: Vendor ID, when available. format: int32 vendorName: type: string description: Vendor name, when available. strainClassificationId: type: integer description: Strain classification ID, when available. format: int32 strainClassificationLabel: type: string description: Strain classification label, when available. strainFamily: type: string description: Strain family, when available. example: HYBRID enum: - INDICA - SATIVA - HYBRID posCategoryId: type: integer description: POS category ID, when available. format: int32 posCategoryLabel: type: string description: POS category label, when available. posSubCategoryId: type: integer description: POS subcategory ID, when available. format: int32 posSubCategoryLabel: type: string description: POS subcategory label, when available. posIdentifier: type: string description: Product identifier from the source POS system, or an empty string when unavailable. universalCategoryId: type: integer description: Mapped universal category ID, when available. format: int32 universalCategoryLabel: type: string description: Mapped universal category label, when available. posBrandId: type: integer description: POS brand ID, when available. format: int32 posBrandName: type: string description: POS brand name, when available. sku: type: string description: Product SKU from the source POS system, or an empty string when unavailable. caseSize: type: integer description: Minimum order/case size, when available. format: int32 example: 12 description: Product metadata for this inventory row. ProductInventoryResponse: type: object properties: predictedDaysOnHand: type: integer description: Predicted days on hand based on current inventory and sales velocity. format: int32 averageThcPercentageSold: type: number description: Average THC percentage for units sold. format: double distributionFulfillmentStatus: type: string description: Distribution fulfillment status, when distribution data is available. enum: - PARTIAL - UNAVAILABLE - AVAILABLE - NO_DISTRIBUTION continueToCarry: type: boolean description: 'Whether the product should continue to be carried at the store. When `false`, the product is discontinued and will not be recommended for reorders. ' medPrice: type: number description: Medical unit price, when available. format: double recPrice: type: number description: Recreational unit price, when available. format: double unitsNeeded: type: integer description: Units needed to reach the replenishment target. format: int32 unitsSold: type: integer description: Units sold in the selected lookback window. format: int32 unitsToOrder: type: integer description: Recommended units to order. format: int32 dollarsSold: type: number description: Sales dollars in the selected lookback window. format: double discountDollars: type: number description: Discount dollars in the selected lookback window. format: double averageDiscountPercent: type: number description: Average discount percentage in the selected lookback window. format: double averageMargin: type: number description: Average margin percentage. format: double estimatedProfit: type: number description: Estimated profit dollars. format: double inventoryCost: type: number description: Current inventory cost dollars. format: double agedInventoryCost: type: number description: Cost dollars for inventory aged 90 or more days. format: double averageThcPercentageOnHand: type: number description: Average THC percentage for units currently on hand. format: double unitCost: type: number description: Unit cost, when available. format: double currentInventory: type: integer description: Current inventory units on hand. format: int32 example: 24 storeName: type: string description: Store/location display name for this inventory row. unitPrice: type: number description: Unit price, when available. format: double storeId: type: string description: Store/location UUID for this inventory row. format: uuid sellableUnitsOnHand: type: integer description: Sellable units on hand. format: int32 unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 example: 10 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 example: 8 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 example: 4 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 example: 2 unitsPerDay: type: number description: Average units sold per day in the selected lookback window. format: double sellableInventory: type: integer description: Current sellable inventory units. format: int32 example: 20 daysOutOfStock: type: integer description: Number of days the product was out of stock. format: int32 example: 3 productDetail: $ref: '#/components/schemas/ProductDetailResponse' lastActivity: type: string description: Most recent inventory or sales activity timestamp, when available. format: date-time firstSold: type: string description: First sale timestamp in the selected lookback data, when available. format: date-time lastSold: type: string description: Most recent sale timestamp in the selected lookback data, when available. format: date-time unitsAtDistro: type: integer description: Units available at a fulfillment/distribution location. format: int32 description: Records for the current page. LimitOffsetResponseProductLineInventoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/ProductLineInventoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true ProductLineInventoryResponse: type: object properties: status: type: string description: Most recent report status for this product line, when available. enum: - OUT_OF_DATE - LOW_STOCK predictedDaysOnHand: type: integer description: Predicted days on hand. Empty when unitsPerDay is zero. format: int32 targetProductDepth: type: integer description: Target number of distinct products for this store's product line. format: int32 averageThcPercentageSold: type: number description: Average THC percentage for units sold. Empty when no units were sold. format: double unitsNeeded: type: integer description: Units needed to reach the replenishment target. format: int32 unitsSold: type: integer description: Units sold in the selected lookback window. format: int32 dollarsSold: type: number description: Gross dollars sold in the selected lookback window. format: double averageDiscountPercent: type: number description: Average discount percentage in the selected lookback window. format: double estimatedProfit: type: number description: Estimated profit in the selected lookback window. format: double inventoryCost: type: number description: Current inventory cost for the product line. format: double agedInventoryCost: type: number description: Current inventory cost for inventory aged more than 60 days. format: double averageThcPercentageOnHand: type: number description: Average THC percentage for units currently on hand. Empty if none on hand. format: double currentInventory: type: integer description: Current inventory units on hand across products in the line. format: int32 averageMarginPercent: type: number description: Average margin percentage in the selected lookback window. format: double sativaProductDepth: type: integer description: Current number of distinct sativa products. format: int32 hybridProductDepth: type: integer description: Current number of distinct hybrid products. format: int32 indicaProductDepth: type: integer description: Current number of distinct indica products. format: int32 storeName: type: string description: Store/location display name for this inventory row. storeId: type: string description: Store/location UUID for this product-line inventory row. format: uuid reportRunAt: type: string description: Timestamp when the product-line report was last calculated. format: date-time unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 unitsPerDay: type: number description: Average units sold per day for the product line. format: double unitsAtDistro: type: integer description: Units available at a fulfillment/distribution location. format: int32 productLineDetail: $ref: '#/components/schemas/ProductLineDetailResponse' productLineId: type: integer description: Product line id for this product-line inventory row. format: int32 currentDepth: type: integer description: Current number of distinct products. format: int32 description: Records for the current page. LimitOffsetResponsePackageResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/PackageResponse' hasMore: type: boolean description: True when more records are available after this page. example: true PackageResponse: type: object properties: posId: type: string description: Package identifier from the POS. id: type: integer description: Internal package row ID. format: int64 example: 12345 posAddedAt: type: string description: Timestamp when the package was added in the POS, when available. format: date-time posCreatedAt: type: string description: Timestamp when the package was created in the POS, when available. format: date-time productId: type: integer description: Internal product ID associated with the package. format: int32 example: 987 universalBrandId: type: integer description: Mapped universal brand ID, when available. format: int32 universalBrandName: type: string description: Mapped universal brand name, when available. posCategoryId: type: integer description: POS category ID, when available. format: int32 posCategoryLabel: type: string description: POS category label, when available. posSubCategoryId: type: integer description: POS subcategory ID, when available. format: int32 posSubCategoryLabel: type: string description: POS subcategory label, when available. lastActivityAt: type: string description: Most recent product stock activity timestamp, when available. format: date-time productName: type: string description: Product display name. barcode: type: string description: Package barcode, when available. daysAged: type: integer description: Package age in days, when available. format: int32 example: 42 receivedAt: type: string description: Timestamp when the package was received, when available. format: date-time regulatoryId: type: string description: Regulatory package identifier, when available. posUnitCost: type: number description: POS unit cost for this package, when available. format: double thcPercentage: type: number description: THC percentage for this package, when available. format: double universalCategoryId: type: integer description: Mapped universal category ID, when available. format: int32 universalCategoryLabel: type: string description: Mapped universal category label, when available. posBrandId: type: integer description: POS brand ID, when available. format: int32 posBrandName: type: string description: POS brand name, when available. currentInventory: type: integer description: Current package inventory units. format: int32 example: 24 storeName: type: string description: Store/location display name for this package. storeId: type: string description: Store/location UUID for this package. format: uuid description: Records for the current page. LimitOffsetResponsePosCategoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/PosCategoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true PosCategoryResponse: type: object properties: name: type: string description: POS category machine name. example: flower label: type: string description: POS category display label. example: Flower mappedCategoryId: type: integer description: Mapped universal category ID, when this POS category has a mapping. format: int32 isSubCategory: type: boolean description: True when this POS category is a subcategory. example: false posIdentifier: type: string description: Category identifier from the source POS system. example: category-123 mappedCategoryName: type: string description: Mapped universal category machine name, or an empty string when unmapped. mappedCategoryLabel: type: string description: Mapped universal category display label, or an empty string when unmapped. description: Records for the current page. LimitOffsetResponsePosBrandResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/PosBrandResponse' hasMore: type: boolean description: True when more records are available after this page. example: true PosBrandResponse: type: object properties: name: type: string description: POS brand display name. example: Acme id: type: integer description: Happy Buyers POS brand ID. format: int32 example: 42 productCount: type: integer description: Number of products currently associated with this POS brand. format: int32 example: 17 mappedUniversalBrandId: type: integer description: Mapped universal brand ID, when this POS brand has a mapping. format: int32 posIdentifier: type: string description: Brand identifier from the source POS system. example: brand-123 mappedUniversalBrandName: type: string description: Mapped universal brand name, or an empty string when unmapped. description: Records for the current page. LimitOffsetResponseOrderResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/OrderResponse' hasMore: type: boolean description: True when more records are available after this page. example: true LimitOffsetResponseOrderItemResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/OrderItemResponse' hasMore: type: boolean description: True when more records are available after this page. example: true LimitOffsetResponseOrderInvoiceResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/OrderInvoiceResponse' hasMore: type: boolean description: True when more records are available after this page. example: true LimitOffsetResponseStoreHistoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/StoreHistoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true StoreHistoryResponse: type: object properties: startDate: type: string description: Inclusive start date for this history period. format: date example: '2026-01-05' endDate: type: string description: Inclusive end date for this history period. format: date example: '2026-01-11' periodType: type: string description: Inventory history period granularity. example: WEEKLY enum: - WEEKLY - MONTHLY predictedDaysOnHand: type: integer description: Predicted days on hand based on inventory and sales velocity. format: int32 createdAt: type: string description: Creation timestamp for the underlying inventory health data. format: date-time updatedAt: type: string description: Most recent update timestamp for the underlying inventory health data. format: date-time unitsSold: type: integer description: Units sold in the projected inventory health period. format: int32 example: 120 dollarsSold: type: number description: Sales dollars in the projected inventory health period. format: double example: 3200.0 discountDollars: type: number description: Discount dollars in the projected inventory health period. format: double example: 150.0 averageDiscountPercent: type: number description: Average discount percentage. format: double example: 4.5 averageMargin: type: number description: Average margin percentage. format: double example: 42.5 inventoryCost: type: number description: Current inventory cost dollars. format: double example: 1200.0 agedInventoryCost: type: number description: Cost dollars for inventory aged 90 or more days. format: double example: 300.0 storeName: type: string description: Store/location display name. example: Downtown storeId: type: string description: Store/location UUID for this inventory health row. format: uuid totalProfit: type: number description: Total profit dollars. format: double example: 875.25 percentShareOfProfit: type: number description: Percent share of total profit for the current result set. format: double percentShareOfAgedInventoryCost: type: number description: Percent share of aged inventory cost for the current result set. format: double percentShareOfDollarsSold: type: number description: Percent share of dollars sold for the current result set. format: double percentShareOfUnitsSold: type: number description: Percent share of units sold for the current result set. format: double unitsInStock: type: integer description: Current inventory units on hand. format: int32 example: 80 sellableUnitsInStock: type: integer description: Current sellable inventory units on hand. format: int32 example: 72 unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 example: 25 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 example: 20 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 example: 15 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 example: 10 unitsPerDay: type: number description: Average units sold per day. format: double example: 6.25 description: Records for the current page. LimitOffsetResponsePosBrandHistoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/PosBrandHistoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true PosBrandHistoryResponse: type: object properties: startDate: type: string description: Inclusive start date for this history period. format: date example: '2026-01-05' endDate: type: string description: Inclusive end date for this history period. format: date example: '2026-01-11' periodType: type: string description: Inventory history period granularity. example: WEEKLY enum: - WEEKLY - MONTHLY posBrandId: type: integer description: POS brand ID. format: int32 example: 99 posBrandName: type: string description: POS brand name. example: Blue River predictedDaysOnHand: type: integer description: Predicted days on hand based on inventory and sales velocity. format: int32 createdAt: type: string description: Creation timestamp for the underlying inventory health data. format: date-time updatedAt: type: string description: Most recent update timestamp for the underlying inventory health data. format: date-time unitsSold: type: integer description: Units sold in the projected inventory health period. format: int32 example: 120 dollarsSold: type: number description: Sales dollars in the projected inventory health period. format: double example: 3200.0 discountDollars: type: number description: Discount dollars in the projected inventory health period. format: double example: 150.0 averageDiscountPercent: type: number description: Average discount percentage. format: double example: 4.5 averageMargin: type: number description: Average margin percentage. format: double example: 42.5 inventoryCost: type: number description: Current inventory cost dollars. format: double example: 1200.0 agedInventoryCost: type: number description: Cost dollars for inventory aged 90 or more days. format: double example: 300.0 storeName: type: string description: Store/location display name. example: Downtown storeId: type: string description: Store/location UUID for this inventory health row. format: uuid totalProfit: type: number description: Total profit dollars. format: double example: 875.25 percentShareOfProfit: type: number description: Percent share of total profit for the current result set. format: double percentShareOfAgedInventoryCost: type: number description: Percent share of aged inventory cost for the current result set. format: double percentShareOfDollarsSold: type: number description: Percent share of dollars sold for the current result set. format: double percentShareOfUnitsSold: type: number description: Percent share of units sold for the current result set. format: double unitsInStock: type: integer description: Current inventory units on hand. format: int32 example: 80 sellableUnitsInStock: type: integer description: Current sellable inventory units on hand. format: int32 example: 72 unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 example: 25 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 example: 20 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 example: 15 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 example: 10 unitsPerDay: type: number description: Average units sold per day. format: double example: 6.25 description: Records for the current page. CategoryResponse: type: object properties: categoryId: type: integer description: POS category ID. format: int32 example: 42 categoryName: type: string description: POS category name. example: Flower predictedDaysOnHand: type: integer description: Predicted days on hand based on inventory and sales velocity. format: int32 createdAt: type: string description: Creation timestamp for the underlying inventory health data. format: date-time updatedAt: type: string description: Most recent update timestamp for the underlying inventory health data. format: date-time unitsSold: type: integer description: Units sold in the projected inventory health period. format: int32 example: 120 dollarsSold: type: number description: Sales dollars in the projected inventory health period. format: double example: 3200.0 discountDollars: type: number description: Discount dollars in the projected inventory health period. format: double example: 150.0 averageDiscountPercent: type: number description: Average discount percentage. format: double example: 4.5 averageMargin: type: number description: Average margin percentage. format: double example: 42.5 inventoryCost: type: number description: Current inventory cost dollars. format: double example: 1200.0 agedInventoryCost: type: number description: Cost dollars for inventory aged 90 or more days. format: double example: 300.0 storeName: type: string description: Store/location display name. example: Downtown storeId: type: string description: Store/location UUID for this inventory health row. format: uuid totalProfit: type: number description: Total profit dollars. format: double example: 875.25 percentShareOfProfit: type: number description: Percent share of total profit for the current result set. format: double percentShareOfAgedInventoryCost: type: number description: Percent share of aged inventory cost for the current result set. format: double percentShareOfDollarsSold: type: number description: Percent share of dollars sold for the current result set. format: double percentShareOfUnitsSold: type: number description: Percent share of units sold for the current result set. format: double unitsInStock: type: integer description: Current inventory units on hand. format: int32 example: 80 sellableUnitsInStock: type: integer description: Current sellable inventory units on hand. format: int32 example: 72 unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 example: 25 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 example: 20 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 example: 15 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 example: 10 unitsPerDay: type: number description: Average units sold per day. format: double example: 6.25 description: Records for the current page. LimitOffsetResponseCategoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/CategoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true CategoryHistoryResponse: type: object properties: categoryId: type: integer description: POS category ID. format: int32 example: 42 startDate: type: string description: Inclusive start date for this history period. format: date example: '2026-01-05' endDate: type: string description: Inclusive end date for this history period. format: date example: '2026-01-11' periodType: type: string description: Inventory history period granularity. example: WEEKLY enum: - WEEKLY - MONTHLY categoryName: type: string description: POS category name. example: Flower predictedDaysOnHand: type: integer description: Predicted days on hand based on inventory and sales velocity. format: int32 createdAt: type: string description: Creation timestamp for the underlying inventory health data. format: date-time updatedAt: type: string description: Most recent update timestamp for the underlying inventory health data. format: date-time unitsSold: type: integer description: Units sold in the projected inventory health period. format: int32 example: 120 dollarsSold: type: number description: Sales dollars in the projected inventory health period. format: double example: 3200.0 discountDollars: type: number description: Discount dollars in the projected inventory health period. format: double example: 150.0 averageDiscountPercent: type: number description: Average discount percentage. format: double example: 4.5 averageMargin: type: number description: Average margin percentage. format: double example: 42.5 inventoryCost: type: number description: Current inventory cost dollars. format: double example: 1200.0 agedInventoryCost: type: number description: Cost dollars for inventory aged 90 or more days. format: double example: 300.0 storeName: type: string description: Store/location display name. example: Downtown storeId: type: string description: Store/location UUID for this inventory health row. format: uuid totalProfit: type: number description: Total profit dollars. format: double example: 875.25 percentShareOfProfit: type: number description: Percent share of total profit for the current result set. format: double percentShareOfAgedInventoryCost: type: number description: Percent share of aged inventory cost for the current result set. format: double percentShareOfDollarsSold: type: number description: Percent share of dollars sold for the current result set. format: double percentShareOfUnitsSold: type: number description: Percent share of units sold for the current result set. format: double unitsInStock: type: integer description: Current inventory units on hand. format: int32 example: 80 sellableUnitsInStock: type: integer description: Current sellable inventory units on hand. format: int32 example: 72 unitsAged0To30: type: integer description: Current inventory units aged 0 to 30 days. format: int32 example: 25 unitsAged31To60: type: integer description: Current inventory units aged 31 to 60 days. format: int32 example: 20 unitsAged61To90: type: integer description: Current inventory units aged 61 to 90 days. format: int32 example: 15 unitsAged90Plus: type: integer description: Current inventory units aged 90 or more days. format: int32 example: 10 unitsPerDay: type: number description: Average units sold per day. format: double example: 6.25 description: Records for the current page. LimitOffsetResponseCategoryHistoryResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/CategoryHistoryResponse' hasMore: type: boolean description: True when more records are available after this page. example: true DailySalesMetadataResponse: type: object properties: day: type: string description: Local sales date in the organization's configured timezone. format: date example: '2026-01-02' unitsSold: type: integer description: Total units sold on this day. format: int32 example: 12 dollarsSold: type: number description: Gross sales dollars on this day. format: double example: 240.0 discountDollars: type: number description: Discount dollars applied to sales on this day. format: double example: 18.5 thcPercentageSold: type: number description: Sum of sold package THC percentages used with known-THC units to calculate a weighted daily average. format: double example: 252.0 quantitySoldWithTHCPercentage: type: integer description: Units sold with a known THC percentage. format: int32 example: 10 inStockAtEndOfDay: type: boolean description: Whether the product was determined to be in stock at the end of this day. Empty when stock status could not be determined. example: true LimitOffsetResponseBlockoutDateResponse: type: object properties: limit: type: integer description: Maximum number of records requested for this page. format: int32 example: 100 offset: type: integer description: Zero-based result offset used for this page. format: int32 example: 0 totalCount: type: integer description: Total number of matching records before pagination is applied. format: int32 example: 250 results: type: array description: Records for the current page. items: $ref: '#/components/schemas/BlockoutDateResponse' hasMore: type: boolean description: True when more records are available after this page. example: true securitySchemes: ApiKeyAuth: type: apiKey description: External API key issued by Happy Buyers. Send the key in the hca-api-key header. name: hca-api-key in: header