---
title: "Add costs to products"
method: POST
path: "/public/v1/products/add-costs"
tags: ["Cost"]
---

# Add costs to products

`POST /public/v1/products/add-costs`

## Request

- Base URL: `https://app.distru.com`
- URL: `https://app.distru.com/public/v1/products/add-costs`
- Auth: HTTP bearer

Add one or more costs to each of the given product-tracked products, and return the affected [products](#model-product) with their updated cost totals.

`product_ids` is a non-empty list of product IDs. Every product must exist, be accessible to the authenticated company, and be product-tracked (a product tracked by batch or package is rejected). The cost lands on each product's active-status stock quantity. `location_ids` optionally scopes which locations' stock the cost applies to; omit it to apply across all locations.

Each entry in `costs` records one cost against the selected records; the amount added to a record's cost basis is `cost_per_unit` × `quantity`. Every entry in the list is applied to every selected record, so N records and M cost entries create N × M cost entries. Fields:

- `cost_type_id` (**required**): the cost type to apply, as its ID from GET /public/v1/cost-types. Must exist and be accessible to the authenticated company.
- `quantity` (**required**): how many units of the cost type to apply, as a decimal. Must be greater than 0.
- `cost_per_unit` (optional): the per-unit amount, as a decimal. When omitted, the cost type's own cost per unit is used. Must be omitted for cost types with a locked cost per unit (those that don't allow inline editing); sending it for such a type is rejected. It is only required when an inline-editable cost type has no cost per unit of its own.
- `description` (optional): free-form text stored on the cost.


When `distribute_by_quantity` is `true`, the total of each cost (`cost_per_unit` × `quantity`) is split across the selected records in proportion to each record's quantity, instead of applying the full cost to every record. Quantities are converted to a common unit before the split, so all selected records must share the same unit type category. Selecting a single record is a no-op (the whole cost lands on it). Defaults to `false` when omitted, applying the same cost in full to each selected record.


This is applied synchronously and atomically: a 200 means every cost has already been recorded and the response body reflects the updated records — there is nothing to poll. If any id, cost, or validation fails, the entire request is rejected and nothing is changed.

Adding costs is additive, not an upsert. Each call records new cost entries and raises the recorded cost basis (COGS) of the selected inventory's stock; sending the same body twice applies the cost twice. Cost entries created here cannot be edited or removed through this endpoint. The effect is confined to Distru cost accounting — it does not push to, pull from, or alter Metrc or BioTrack.


Common errors (HTTP 400 unless noted):

- The `*_ids` list is empty.
- One or more ids don't exist or aren't accessible to the authenticated company.
- A record isn't tracked by the endpoint's method (e.g. a product that isn't product-tracked, or a batch whose product isn't batch-tracked).
- A record has no quantity to add a cost to.
- `cost_type_id` is missing or unknown, or `quantity` is missing or not greater than 0.
- `cost_per_unit` is set for a cost type with a locked cost per unit, or missing when required.
- `distribute_by_quantity` is used across records with mixed unit type categories.
- Cost Accounting module is not enabled, or the user lacks `costs_permissions_apply_to_inventory` (HTTP 403).


Required permission: `costs_permissions_apply_to_inventory`.

## Request body

- AddProductCostsRequest
  - `costs` CostEntryInput[], required: Required. Non-empty list of costs; each entry is applied to every listed product
    - `cost_per_unit` number: Per-unit amount as a decimal (e.g. "10.00"). When omitted, the cost type's own cost per unit is used. Must be omitted for cost types with a locked cost per unit (those that don't allow inline editing) — sending it for such a type is rejected. It is only required when an inline-editable cost type has no cost per unit of its own
    - `cost_type_id` string, required: Required. The cost type to apply, given as its ID from GET /public/v1/cost-types. Must exist and be accessible to the authenticated company
    - `description` string: Optional free-form text stored on the cost
    - `quantity` number, required: Required. How many units of the cost type to apply, as a decimal (e.g. "2.5"). Must be greater than 0. The amount added to each record's cost basis is cost_per_unit × quantity
  - `distribute_by_quantity` boolean: When true, split each cost across the listed products in proportion to each product's active quantity (all products must share the same unit type category). Defaults to false when omitted, applying the full cost to every product
  - `location_ids` string[]: Optional list of location IDs scoping which locations' stock the cost applies to. Omit to apply across all locations; an explicit empty list is rejected
  - `product_ids` string[], required: Required. Non-empty list of product IDs; every one must exist, be accessible to the authenticated company, and be product-tracked. Each cost is applied to all listed products

## Response `200`

The affected products

- Products: A collection of Products
  - `data` Product[]: Products
    - `batches_with_active_quantity_by_location` BatchesWithActiveQuantityByLocation[]: The product's batches that hold active quantity, grouped by location. Present only when the request passes `include_batches_with_active_quantity_by_location=true`; the field is absent otherwise. Empty array when the product is not batch-tracked or holds no active batches.
      - `batches` BatchCompactWithQuantityActive[], required: The product's batches with active quantity at this location, each carrying its active quantity there, ordered by id.
        - `batch_number` string: The batch number for this batch, or null when none is set
        - `id` string, required: ID for this batch
        - `name` string, required: Human readable name for this batch
        - `quantity_active` string, required: The batch's active on-hand quantity at this location, as a decimal string (e.g. "100"). Always positive.
      - `location` LocationCompact, required: A compact reference to a [location](#model-location) as nested inside another entity in Distru. Use its `id` to fetch the full location from the locations endpoint.
        - `address` string, required: The full address as a single formatted line, built from the street, apt, city, state, zip, and country fields
        - `company_id` string: ID of the [company](#model-company) that owns this location
        - `id` string, required: ID for this location
        - `license_id` string: ID of the [license](#model-license) this location is associated with, or null when the location has no license
        - `name` string, required: Human readable name for this location
    - `bill_of_materials` BillOfMaterials: A product's bill of materials (recipe of inputs and additional costs)
      - `costs` BillOfMaterialsCost[], required: The additional (non-material) costs this recipe applies, ordered oldest first. Empty when none.
        - `cost_type` BillOfMaterialsCostType: The cost type applied by a bill-of-materials cost
          - `cost_per_unit` string: The cost per unit, as a decimal string (e.g. "5.00"). Present only when the caller has the `costs_permissions_view_cost_types_cost_per_unit` permission; the field is omitted otherwise.
          - `id` string, required: ID for this cost type
          - `name` string, required: Human readable name for this cost type
          - `unit_type` UnitType: A unit of measure used for quantities and pricing (e.g. Gram, Each, Pound). This is the compact reference carrying just id and name; the full unit type (its measurement category and conversion factor) is available from the unit types endpoint.
            - `id` string, required: ID for this unit type
            - `name` string, required: Human readable name for this unit type
        - `description` string: Free-text description of this cost, or null when none was entered
        - `id` string, required: ID for this cost
        - `quantity` string, required: How many units of the cost type this line applies, as a decimal string (e.g. "3"). Multiply by the cost type's `cost_per_unit` to get the line total.
      - `description` string: Free-text description of this bill of materials, or null when none was entered
      - `dynamic_inputs` BillOfMaterialsDynamicInput[], required: The inputs that select products by attribute (category, strain, tag, and so on) rather than naming a specific product. Empty when this recipe has none.
        - `id` string, required: ID for this input
        - `product_categories` ProductCategoryCompact[], required: The product categories this input matches on, or empty when it does not match on category. A product must match every non-empty criterion.
          - `id` string, required: ID for this category
          - `name` string, required: Human readable name for this category
          - `official_product_category_id` string: The ID of Distru's standardized (official) category this maps to, used to normalize categories across companies. Null when this category is not mapped to a standardized category.
        - `product_groups` ProductGroupCompact[], required: The product groups this input matches on, or empty when it does not match on group.
          - `id` string, required: ID for this product group
          - `name` string, required: The name of this product group
        - `product_subcategories` ProductSubcategoryCompact[], required: The product subcategories this input matches on, or empty when it does not match on subcategory.
          - `id` string, required: ID for this subcategory
          - `name` string, required: Human readable name for this subcategory
        - `quantity` string, required: How much this recipe consumes of whatever product matches, as a decimal string (e.g. "2.5").
        - `strains` Strain[], required: The strains this input matches on, or empty when it does not match on strain.
          - `id` string, required: ID for this strain
          - `inserted_datetime` string, required: The datetime this strain was created at
          - `name` string, required: Name of the strain
          - `strain_type` 'INDICA' | 'INDICA_DOMINANT' | 'SATIVA' | 'SATIVA_DOMINANT' | 'HYBRID' | 'HIGH_CBD': The genetic classification of the strain. Null when unset.
          - `updated_datetime` string, required: The datetime this strain was last updated at
        - `tags` ProductTagRef[], required: The tags this input matches on, or empty when it does not match on tag.
          - `id` string, required: ID for this tag
          - `name` string, required: The name of this tag
        - `unit_type` UnitType: A unit of measure used for quantities and pricing (e.g. Gram, Each, Pound). This is the compact reference carrying just id and name; the full unit type (its measurement category and conversion factor) is available from the unit types endpoint.
          - `id` string, required: ID for this unit type
          - `name` string, required: Human readable name for this unit type
      - `id` string, required: ID for this bill of materials
      - `name` string, required: Human readable name for this bill of materials
      - `product_inputs` BillOfMaterialsProductInput[], required: The inputs that name a specific product to consume. Empty when this recipe has none. See `dynamic_inputs` for inputs that select products by attribute instead.
        - `id` string, required: ID for this input
        - `product` Product (recursive)
        - `quantity` string, required: How much of the product this recipe consumes, in the product's unit, as a decimal string (e.g. "2.5").
    - `brand` CompanyCompact: A lightweight reference to a company — just its identity — embedded on other entities (orders, invoices, products, etc.) to point at the full company without inlining it. The company is a trading partner (a customer or vendor) in your Distru network. Use the id to fetch its full details from the companies endpoint.
      - `id` string, required: ID for this company
      - `name` string, required: Human readable name for this company
      - `tasks` TaskCompact[], required: The tasks linked to this company that the authenticated user can see: none without `tasks_permissions_view`, and only those their team restrictions allow. An empty array when there are none.
        - `due_datetime` string, required: When the task is due, as a UTC ISO8601 datetime.
        - `id` string, required: ID of the task.
        - `name` string, required: The task's name.
        - `status` 'ACTIVE' | 'COMPLETED', required: The task's status.
      - `updated_datetime` string, required: ISO 8601 datetime this company was last updated at
    - `category` ProductCategoryCompact: The top-level classification of a product (e.g. Flower, Edibles, Concentrates). Categories are defined per company and can be organized into subcategories.
      - `id` string, required: ID for this category
      - `name` string, required: Human readable name for this category
      - `official_product_category_id` string: The ID of Distru's standardized (official) category this maps to, used to normalize categories across companies. Null when this category is not mapped to a standardized category.
    - `creator` User: A member of your Distru team — the account behind actions like owning or creating records.
      - `banned` boolean: True when this user has been banned and can no longer sign in
      - `deleted_at` string: ISO 8601 datetime this user was deleted at, or null when the user is not deleted
      - `email` string, required: The email address of this user
      - `full_name` string: The full name of this user, or null when not set
      - `id` string, required: ID for this user
      - `inserted_datetime` string, required: ISO 8601 datetime this user was created at
      - `role` Role: A permission role that determines what a [user](#model-user) can do in Distru.
        - `id` string, required: ID for this role
        - `name` string, required: Name of the role (e.g. Admin, Sales)
      - `updated_datetime` string, required: ISO 8601 datetime this user was last updated at. It also moves on changes not shown here, such as the user signing in.
    - `custom_data` CustomField[], required: The custom data for this product
      - `id` integer, required: The ID of this custom field's definition (matches an `id` from `GET /public/v1/custom-fields`). An integer.
      - `name` string, required: The name of this custom field
      - `value` string: This record's value for the custom field, as a string, or null when no value has been set for this record
    - `deleted_at` string: ISO 8601 datetime the product was soft-deleted at, or null when the product has not been deleted
    - `description` string: Plain-text description of this product, or null when none was entered. Always null together with `description_markdown` and non-null together with it — one is never set without the other.
    - `description_markdown` string: The same description in Markdown. Null when `description` is null; populated whenever `description` is populated.
    - `external_name` string: Customer-facing name shown on DistruCommerce menus and the Order Tracker, or null when none is set (it does not fall back to `name`)
    - `gross_weight` string: The gross weight of one unit including packaging, as a decimal string, measured in `gross_weight_unit_type`. Null when not set; it is always set together with `gross_weight_unit_type` and null together with it.
    - `gross_weight_unit_type` UnitType: A unit of measure used for quantities and pricing (e.g. Gram, Each, Pound). This is the compact reference carrying just id and name; the full unit type (its measurement category and conversion factor) is available from the unit types endpoint.
      - `id` string, required: ID for this unit type
      - `name` string, required: Human readable name for this unit type
    - `id` string, required: ID for this product
    - `images` Image[]: The images associated with this product, in display order: by `rank`, lowest first (the first is the primary image), then by id. Images with no rank come last. Empty array when the product has no images.
      - `id` string, required: ID for this image
      - `name` string: Original file name of this image, or null when unknown
      - `rank` integer: Display slot of this image among the product's images, 0 to 3; the lowest rank is the primary image. Null when unranked
      - `url` string: URL to the full-size (original) image file, or null when no file is available
    - `inserted_datetime` string, required: The datetime this product was created at
    - `inventory_tracking_method` 'PACKAGE' | 'BATCH' | 'PRODUCT', required: How this product's inventory is tracked. One of: `BATCH` (grouped into batches sharing traits such as expiration dates and test results), `PACKAGE` (inventory is defined by packages), `PRODUCT` (ungrouped; inventory exists directly on the product).
    - `is_active` boolean, required: True when the product is active. False when it has been marked inactive (archived), which hides it from most product pickers.
    - `is_featured` boolean, required: True when the product is flagged as featured
    - `leaflink_product_id` integer: The LeafLink product ID this product is synced to, or null if not synced
    - `menu_visibility` 'DO_NOT_INCLUDE' | 'INCLUDE_IN_ALL' | 'INCLUDE_IN_SELECT': Which menus this product is shown in. One of `DO_NOT_INCLUDE`, `INCLUDE_IN_ALL`, `INCLUDE_IN_SELECT` (same values accepted by the upsert endpoint); defaults to `DO_NOT_INCLUDE`. The `menus` field below lists the specific menus the product belongs to — this is how you see which menus when the value is `INCLUDE_IN_SELECT`. Setting `INCLUDE_IN_ALL` adds the product to every menu, and a menu created later gets it too, except a copy of a menu it isn't on, or a new (not copied) menu made by a user who can't see the product: read `menus` for where it really is.
    - `menus` CompactMenu[]: Menus this product is on, ordered by menu creation time then id (includes inactive menus). Follows `menu_visibility`: the chosen menus for `INCLUDE_IN_SELECT`, every menu but the exceptions listed under `menu_visibility` for `INCLUDE_IN_ALL`, and usually none for `DO_NOT_INCLUDE`. A change made by a team-restricted user never removes the product from menus that user can't see, so those stay listed.
      - `id` string, required: ID of the menu
      - `name` string, required: Display name of the menu
    - `msrp` string: Manufacturer's suggested retail price, as a decimal string (e.g. "19.99"), or null when not set
    - `name` string, required: Human readable name for this product
    - `owner` User: A member of your Distru team — the account behind actions like owning or creating records.
      - `banned` boolean: True when this user has been banned and can no longer sign in
      - `deleted_at` string: ISO 8601 datetime this user was deleted at, or null when the user is not deleted
      - `email` string, required: The email address of this user
      - `full_name` string: The full name of this user, or null when not set
      - `id` string, required: ID for this user
      - `inserted_datetime` string, required: ISO 8601 datetime this user was created at
      - `role` Role: A permission role that determines what a [user](#model-user) can do in Distru.
        - `id` string, required: ID for this role
        - `name` string, required: Name of the role (e.g. Admin, Sales)
      - `updated_datetime` string, required: ISO 8601 datetime this user was last updated at. It also moves on changes not shown here, such as the user signing in.
    - `packages_with_active_quantity_by_location` PackagesWithActiveQuantityByLocation[]: The product's packages that hold active quantity, grouped by location. Present only when the request passes `include_packages_with_active_quantity_by_location=true`; the field is absent otherwise. Empty array when the product is not package-tracked or holds no active packages.
      - `location` LocationCompact, required: A compact reference to a [location](#model-location) as nested inside another entity in Distru. Use its `id` to fetch the full location from the locations endpoint.
        - `address` string, required: The full address as a single formatted line, built from the street, apt, city, state, zip, and country fields
        - `company_id` string: ID of the [company](#model-company) that owns this location
        - `id` string, required: ID for this location
        - `license_id` string: ID of the [license](#model-license) this location is associated with, or null when the location has no license
        - `name` string, required: Human readable name for this location
      - `packages` Package[], required: The product's packages held at this location that have active quantity, ordered by id.
        - `batch_number` string: A free-text batch number set on the package, separate from any compliance tag. Null when none is set.
        - `compliance_label` string: The unique tag assigned by the state compliance system (e.g. the Metrc package tag). Always present for Metrc-tracked packages; may be null for BioTrack packages, and null when the package is not compliance-tracked.
        - `distru_status` 'ACTIVE' | 'ASSEMBLING' | 'DESTROYED' | 'DISCONTINUED' | 'FINISHED' | 'ONHOLD' | 'RETURNING' | 'SELLING' | 'SOLD' | 'TRANSFERRED', required: The package's inventory lifecycle status. One of:• ACTIVE: on hand and available.• ASSEMBLING: allocated to a pending assembly.• SELLING: reserved on an open sales order.• SOLD: consumed by a completed sale.• RETURNING: on an in-progress return.• TRANSFERRED: sent out on a compliance transfer.• ONHOLD: placed on hold in the compliance system.• FINISHED: finished in the compliance system.• DISCONTINUED: discontinued.• DESTROYED: destroyed.
        - `id` string, required: ID for this package in Distru
        - `license_id` string, required: The ID of the [license](#model-license) this package is held under
        - `location_id` string, required: The ID of the [location](#model-location) where this package is physically stored
        - `metrc_id` integer: The Metrc package ID for this package; null for non-Metrc packages
        - `quantity` string, required: The total on-hand quantity of this package, in the package's `unit_type`, as a decimal string (e.g. "100"). Always equals `quantity_active` + `quantity_assembling`. Never negative.
        - `quantity_active` string, required: The freely usable portion of `quantity` — what can be used as an assembly input, moved to another location, added to a sales order, or adjusted down. Equals `quantity` minus `quantity_assembling`, in the package's `unit_type`, as a decimal string. Never negative.
    - `product_group` ProductGroupCompact: A named grouping of products, defined per company (e.g. a brand line or product family).
      - `id` string, required: ID for this product group
      - `name` string, required: The name of this product group
    - `quantity_active` string, required: Total active on-hand quantity of this product across all locations, as a decimal string (e.g. "100"). Sums the product's active stock — positive quantity held at a location. Equals the sum of the per-location amounts in `quantity_active_by_location`, so the two always reconcile. "0" when the product has no active stock. Reserved stock is still physically on-hand, so it is included here; subtract `quantity_reserved` to get `quantity_available`. Sold stock and in-transit stock (held by a user rather than a location) is excluded.
    - `quantity_active_by_location` QuantityActiveByLocation[], required: The product's active on-hand quantity broken down by location — one entry per location holding active stock, ordered by location id. Empty array when the product has no active stock. The entries sum to `quantity_active`.
      - `location` LocationCompact, required: A compact reference to a [location](#model-location) as nested inside another entity in Distru. Use its `id` to fetch the full location from the locations endpoint.
        - `address` string, required: The full address as a single formatted line, built from the street, apt, city, state, zip, and country fields
        - `company_id` string: ID of the [company](#model-company) that owns this location
        - `id` string, required: ID for this location
        - `license_id` string: ID of the [license](#model-license) this location is associated with, or null when the location has no license
        - `name` string, required: Human readable name for this location
      - `quantity` string, required: Active on-hand quantity at this location, as a decimal string (e.g. "100"). Always positive.
    - `quantity_available` string, required: Active on-hand quantity minus reserved quantity, as a decimal string (e.g. "100"). Equals `quantity_active` − `quantity_reserved` — what is on-hand and not already spoken for by unfulfilled sales orders or draft assemblies. Can be negative when more is reserved than is on-hand at a location. "0" when the product has no active stock and nothing reserved.
    - `quantity_available_threshold_max` string: Over-stock alert threshold: Distru flags the product when its available quantity rises above this value. Decimal string, or null when no over-stock alert is configured. When both thresholds are set, this is strictly greater than `quantity_available_threshold_min`.
    - `quantity_available_threshold_min` string: Low-stock alert threshold: Distru flags the product when its available quantity drops below this value. Decimal string, or null when no low-stock alert is configured. When both thresholds are set, this is strictly less than `quantity_available_threshold_max`.
    - `quantity_reserved` string, required: Total quantity of this product currently reserved across all locations, as a decimal string (e.g. "100"). Reserved quantity is a soft hold tracked separately from on-hand stock (`quantity_active`): it rises as unfulfilled sales order items and draft assembly inputs are created, and falls as they are deleted or fulfilled. "0" when nothing is reserved.
    - `sku` string, required: The stock keeping unit (SKU) configured for this product
    - `strain` Strain: A cannabis strain (its genetics), such as "Blue Dream". Products can be linked to a strain to carry its name and type.
      - `id` string, required: ID for this strain
      - `inserted_datetime` string, required: The datetime this strain was created at
      - `name` string, required: Name of the strain
      - `strain_type` 'INDICA' | 'INDICA_DOMINANT' | 'SATIVA' | 'SATIVA_DOMINANT' | 'HYBRID' | 'HIGH_CBD': The genetic classification of the strain. Null when unset.
      - `updated_datetime` string, required: The datetime this strain was last updated at
    - `subcategory` ProductSubcategoryCompact: A finer classification within a product category (e.g. "Pre-Rolls" under Flower).
      - `id` string, required: ID for this subcategory
      - `name` string, required: Human readable name for this subcategory
    - `tags` ProductTagRef[]: The tags associated with this product. Empty array when the product has no tags.
      - `id` string, required: ID for this tag
      - `name` string, required: The name of this tag
    - `tasks` TaskCompact[], required: The tasks linked to this product that the authenticated user can see: none without `tasks_permissions_view`, and only those their team restrictions allow. An empty array when there are none.
      - `due_datetime` string, required: When the task is due, as a UTC ISO8601 datetime.
      - `id` string, required: ID of the task.
      - `name` string, required: The task's name.
      - `status` 'ACTIVE' | 'COMPLETED', required: The task's status.
    - `total_cannabinoid_unit` string: The unit `total_thc` and `total_cbd` are measured in — one of MG (milligrams per unit) or PERCENT (percent by weight). Null when neither potency value is set.
    - `total_cbd` string: Total CBD potency, as a decimal string, measured in `total_cannabinoid_unit`. Null when not set. Non-negative; when the unit is PERCENT it is between 0 and 100. Whenever this is set, `total_cannabinoid_unit` is also set. This is a static label on the product, not a lab-test value.
    - `total_thc` string: Total THC potency, as a decimal string, measured in `total_cannabinoid_unit`. Null when not set. Non-negative; when the unit is PERCENT it is between 0 and 100. Whenever this is set, `total_cannabinoid_unit` is also set. This is a static label on the product, not a lab-test value.
    - `treez_wholesale_price` string: The Treez wholesale price of this product, as a decimal string, or null when not set
    - `unit_cost` string: The cost (purchase price) of one unit of this product, as a decimal string, or null when not set
    - `unit_net_weight` string: The net contents of one unit of this product (weight, volume, or count of the contents), as a decimal string, measured in `unit_net_weight_serving_size_unit_type`. Null when not set.
    - `unit_net_weight_serving_size_unit_type` UnitType: A unit of measure used for quantities and pricing (e.g. Gram, Each, Pound). This is the compact reference carrying just id and name; the full unit type (its measurement category and conversion factor) is available from the unit types endpoint.
      - `id` string, required: ID for this unit type
      - `name` string, required: Human readable name for this unit type
    - `unit_price` string, required: The list (sale) price of one unit of this product, as a decimal string (e.g. "25.00")
    - `unit_serving_size` string: The serving size per unit, as a decimal string, measured in `unit_net_weight_serving_size_unit_type` (the same unit as `unit_net_weight`). Null when not set. When both are set, this is at most `unit_net_weight`.
    - `unit_type` UnitType: A unit of measure used for quantities and pricing (e.g. Gram, Each, Pound). This is the compact reference carrying just id and name; the full unit type (its measurement category and conversion factor) is available from the unit types endpoint.
      - `id` string, required: ID for this unit type
      - `name` string, required: Human readable name for this unit type
    - `units_per_case` string: The number of units of this product packed in one case, as a decimal string (e.g. "12"), or null when not set
    - `upc` string: The UPC barcode of this product, or null when not set
    - `updated_datetime` string, required: The datetime this product was last updated at
    - `vendor` CompanyCompact: A lightweight reference to a company — just its identity — embedded on other entities (orders, invoices, products, etc.) to point at the full company without inlining it. The company is a trading partner (a customer or vendor) in your Distru network. Use the id to fetch its full details from the companies endpoint.
      - `id` string, required: ID for this company
      - `name` string, required: Human readable name for this company
      - `tasks` TaskCompact[], required: The tasks linked to this company that the authenticated user can see: none without `tasks_permissions_view`, and only those their team restrictions allow. An empty array when there are none.
        - `due_datetime` string, required: When the task is due, as a UTC ISO8601 datetime.
        - `id` string, required: ID of the task.
        - `name` string, required: The task's name.
        - `status` 'ACTIVE' | 'COMPLETED', required: The task's status.
      - `updated_datetime` string, required: ISO 8601 datetime this company was last updated at
    - `wholesale_unit_price` number: The wholesale unit price of this product, as a JSON number (e.g. 12.5) rather than a decimal string — unlike `unit_price`. Null when not set.
  - `next_page` string: URL for the next page of results; null when there is no next page

## Other responses

- `400`: Invalid parameters
- `401`: Missing or invalid API token
- `403`: The API token lacks the required permission, or team restrictions hide the record

## Changes

- **2026-10-14** `e8b3a3d07ad5`: 2 info
  - added the required property `data/items/creator/updated_datetime` to the response with the `200` status
  - added the required property `data/items/owner/updated_datetime` to the response with the `200` status

[Operation history](https://skmtc.dev/distru/apis/distru-api/changes/public/v1/products/add-costs/post.md)

---

[API](https://skmtc.dev/distru/apis/distru-api.md) - [All operations](https://skmtc.dev/distru/apis/distru-api/llms.txt) - [OpenAPI document](https://skmtc.dev/distru/apis/distru-api/revisions/9d851a88e82f?raw)
