openapi: 3.2.0 info: title: Happy Buyers External Product Lines 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: Product Lines description: Product-lines 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-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/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' components: schemas: 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 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. 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 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 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. 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 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. 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