---
title: "Upsert a cost type"
method: POST
path: "/public/v1/cost-types"
tags: ["CostType"]
---

# Upsert a cost type

`POST /public/v1/cost-types`

Create or update a single cost type. This is one endpoint for both operations: omit `id` to
create a new cost type (responds 201), or pass the `id` of an existing cost type to update it
(responds 200). The URL and request shape are identical either way.

On create, `name`, `cost_per_unit`, `unit_type_id` and `allow_inline_edits` are all required.
On update the body is sparse — only the fields you send are changed, and any field you omit
keeps its current value. `unit_type_id` is immutable: once a cost type has a unit type it cannot
be reassigned, so sending a different `unit_type_id` on update is rejected. `name` must be unique
within your company, compared case-insensitively, among cost types that are both active and not
soft-deleted; inactive or deleted cost types do not reserve their name, so deactivating or
deleting a cost type frees its name for reuse. `cost_per_unit` must be non-zero (it may be
negative).

A cost type is configuration, not a transaction: this endpoint writes only the cost-type
template itself. It does not create, consume, reserve, or release inventory, and it never syncs
to Metrc or BioTrack — cost types have no compliance identity of their own. It also does not
touch costs already applied from this cost type: each applied cost (on a plant, a package or
batch, an assembly or breakdown output, a purchase item, or a product) snapshots its own amount
and quantity at apply time, so changing this cost type's `cost_per_unit` or `allow_inline_edits`
affects only future applications, never historical cost records.

Required permission: `costs_permissions_manage_cost_types`.

## Request body

- object
  - `id` string — ID of an existing cost type to update. Omit to create a new cost type. When given, the matching cost type in your company is updated in place; the update is sparse, so only the other fields you send are changed.
  - `name` string, required — Display name of the cost type (e.g. "Freight", "Labor"). Required on create. Leading and trailing whitespace is trimmed. Must be unique within your company, compared case-insensitively, among cost types that are both active and not soft-deleted; the name of a deleted or inactive cost type may be reused. On update, omit to leave unchanged.
  - `description` string — Optional free-text description of the cost type. Nullable — send null or omit on create to leave it empty. Leading and trailing whitespace is trimmed. On update, omit to leave the current description unchanged, or send null to clear it.
  - `cost_per_unit` string, required — Default cost amount per one unit of `unit_type_id`, as a decimal string (e.g. "12.50"), with up to 9 decimal places. Required on create. Must be non-zero; may be negative. This is the amount pre-filled when the cost type is applied to a record: when `allow_inline_edits` is true it can be overridden at apply time, otherwise the applied amount is locked to this value. Changing it does not rewrite costs already applied. On update, omit to leave unchanged.
  - `unit_type_id` string, required — ID of the unit of measure this cost is priced per — call GET /public/v1/unit-types to list valid IDs. Required on create and immutable afterwards: once set it cannot be changed, so sending a different value on update is rejected. On update, omit (or resend the same value) to leave unchanged.
  - `active` boolean — Whether the cost type is active and selectable when applying new costs. Defaults to true when omitted on create. Inactive cost types are still returned by the read endpoints. Name uniqueness only considers active cost types, so setting this to false releases the name for another active cost type to use. On update, omit to leave unchanged.
  - `allow_inline_edits` boolean, required — Required on create. When true, the per-unit amount can be overridden each time this cost type is applied to a record; when false, the applied amount is locked to this cost type's `cost_per_unit`. On update, omit to leave unchanged.

## Response `200`

The updated cost type

## Other responses

- `201` — The created cost type
- `400` — Invalid parameters
- `401` — Missing or invalid API token
- `403` — The API token lacks the required permission
- `404` — Not Found

---

[API](https://skmtc.dev/distru/apis/distru-api.md) · [All operations](https://skmtc.dev/distru/apis/distru-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/distru/distru-api/revisions/eb775b7b6ee0/schema)
