APICost

Add costs to packages

POST
/public/v1/packages/add-costs

Add one or more costs to each of the given packages, and return the affected packages with their updated cost totals.

package_ids is a non-empty list of package IDs; every package must exist and be accessible to the authenticated company. Packages carry their own location, so this endpoint does not accept location_ids. Unlike batches and products, the cost lands on the package's full current quantity regardless of its status (active, selling, assembling, etc.).

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.

Authorization

Bearer
headerAuthorizationBearer <token>

API token generated in Distru (Settings → Distru API). Send it on every request as Authorization: Bearer YOUR_API_TOKEN.

Request Body

The packages and costs to apply

application/json
  1. body
costs*array<>

Required. Non-empty list of costs; each entry is applied to every listed package

distribute_by_quantity?boolean

When true, split each cost across the listed packages in proportion to each package's current quantity (all packages must share the same unit type category). Defaults to false when omitted, applying the full cost to every package. Packages carry their own location, so this endpoint accepts no location scoping

package_ids*array<string>

Required. Non-empty list of package IDs; every one must exist and be accessible to the authenticated company. Each cost is applied to all listed packages, against each package's full current quantity regardless of status

Response Body

The affected packages

*/*
  1. response

A collection of Packages

data?array<>

Packages

next_page?string

URL for the next page of results; null when there is no next page

Example Request
curl -X POST 'https://app.distru.com/public/v1/packages/add-costs' \  -H 'Content-Type: application/json' \  -d '{  "costs": [    {      "cost_type_id": "00000000-0000-0000-0000-000000000018",      "quantity": 4    }  ],  "package_ids": [    "00000000-0000-0000-0000-000000000014"  ]}'
Example Response
{  "data": [    {      "batch_number": null,      "biotrack_id": null,      "biotrack_inventory_type_id": null,      "biotrack_net_quantity_per_unit": null,      "biotrack_room_id": null,      "biotrack_status": null,      "biotrack_usable_weight": null,      "compliance_label": "ABCDEF012345670000000064",      "compliance_product_name": "Buds",      "compliance_strain_name": "Cotton Candy",      "compliance_transferred_datetime": null,      "compliance_type": "METRC",      "cost_per_unit_actual": "2.666666666666666666666666666666667",      "cost_per_unit_default": "2.666666666666666666666666666666667",      "creator": {        "banned": false,        "deleted_at": null,        "email": "[email protected]",        "full_name": "FirstName3068 LastName3069",        "id": "00000000-0000-0000-0000-000000000600",        "inserted_datetime": "2026-10-13T15:03:28.609970Z",        "role": {          "id": "00000000-0000-0000-0000-000000000633",          "name": "Admin 1586"        },        "updated_datetime": "2026-10-13T15:03:28.609970Z"      },      "custom_data": [],      "description": null,      "distru_status": "ACTIVE",      "expiration_date": null,      "expiration_datetime": null,      "finished_datetime": null,      "harvest_date": null,      "id": "00000000-0000-0000-0000-00000000001c",      "inactivated_datetime": null,      "inserted_datetime": "2026-10-13T15:03:28.642036Z",      "is_production_batch": false,      "is_test_sample": false,      "is_trade_sample": false,      "lab_testing_state": "NotSubmitted",      "license": {        "active": true,        "expiry_datetime": "2026-11-13T15:03:28.559288Z",        "id": "00000000-0000-0000-0000-000000000050",        "inserted_datetime": "2026-10-13T15:03:28.559372Z",        "issue_datetime": "2026-10-13T15:03:28.559285Z",        "license_number": "CDPH-00000093",        "license_type": "Type 7 Volatile Solvent Extraction"      },      "license_id": "00000000-0000-0000-0000-000000000050",      "location": {        "address": "123 Fake Street, Beverly Hills, CA 90210, US",        "company_id": "00000000-0000-0000-0000-0000000003d2",        "id": "00000000-0000-0000-0000-000000000113",        "license_id": "00000000-0000-0000-0000-000000000050",        "name": "Place 274"      },      "location_id": "00000000-0000-0000-0000-000000000113",      "metrc_archived_date": null,      "metrc_finished_date": null,      "metrc_id": 64,      "metrc_label": "ABCDEF012345670000000064",      "metrc_production_batch_number": null,      "metrc_received_datetime": null,      "metrc_received_from_manifest_number": null,      "metrc_source_harvest_names": null,      "metrc_status": "ACTIVE",      "metrc_transfer_id": null,      "metrc_unit_name": "Ounces",      "owner": {        "banned": false,        "deleted_at": null,        "email": "[email protected]",        "full_name": "FirstName3028 LastName3029",        "id": "00000000-0000-0000-0000-0000000005ec",        "inserted_datetime": "2026-10-13T15:03:28.572661Z",        "role": {          "id": "00000000-0000-0000-0000-00000000061d",          "name": "Admin 1564"        },        "updated_datetime": "2026-10-13T15:03:28.572661Z"      },      "packaged_date": "2014-11-29",      "primary_test_result": null,      "product": {        "id": "466ae148-8da6-48c5-8e3c-89a5fd6b5667",        "name": "Product 524",        "sku": "sku 525",        "updated_datetime": "2026-10-13T15:03:28.595158Z"      },      "product_id": "466ae148-8da6-48c5-8e3c-89a5fd6b5667",      "product_unit_quantity": "3.000000000",      "product_unit_type": {        "id": "00000000-0000-0000-0000-000000002f24",        "name": "Ounce"      },      "quantity": "3.000000000",      "quantity_active": "3.000000000",      "quantity_assembling": "0.000000000",      "quantity_available": "3.000000000",      "status": "active",      "total_cost_actual": "8",      "total_cost_default": "8",      "unit_type": {        "id": "00000000-0000-0000-0000-000000002f24",        "name": "Ounce"      }    }  ]}