---
title: "Create or update a batch"
method: POST
path: "/public/v1/batches"
tags: ["Batch"]
---

# Create or update a batch

`POST /public/v1/batches`

Create or update a single batch. Omit `id` to create a new batch; pass the `id` of an existing batch to update it. Either way the whole operation is atomic — if any field is rejected, nothing is written and the batch is left unchanged.

A batch is Distru's lot for a batch-tracked product: it is the unit that inventory quantity, cost, test results, and (optionally) bins hang off of. You can only create a batch under a product whose inventory-tracking method is batch-tracked; pointing `product_id` at any other product is rejected. Products that track inventory as compliance packages are managed through their package endpoints, not here.

On create, the batch's inventory records are initialized so quantity and cost can begin accumulating against it (a brand-new batch starts with no on-hand quantity — receive a [purchase](#model-purchase), run an assembly, or adjust stock to add inventory). The write is recorded to the batch's activity history. This endpoint does not push to or pull from any state traceability system (Metrc / BioTrack); a 200 reflects the Distru batch record only.

Update is a targeted patch, not a full replace: only the fields you send are changed, and any field you omit keeps its current value. `name` and `product_id` are effectively fixed after creation — `name` is ignored on update, and a batch cannot be moved to a different product. See each field below for its create-time default and its null-vs-omit behavior (notably `bin_ids`).

Required permission: `products_permissions_create` to create, `products_permissions_edit` to update.

## Request body

- object
  - `id` string — The Distru batch ID to update. Omit to create a new batch. When present, it must reference a batch in your company or the request is rejected.
  - `product_id` string — The Distru product ID this batch belongs to. Required on create, and the product must be a batch-tracked product in your company (pointing at a product tracked any other way is rejected). Immutable once the batch exists — on update, omit it or resend the batch's current product; sending a different product id is rejected rather than silently ignored, and a batch can never be moved to another product.
  - `name` string — The batch's short internal name. If omitted on create, Distru auto-assigns the next sequential name for the product (e.g. `B1`, then `B2`). Ignored on update — an existing batch's name cannot be changed here. This is distinct from `batch_number`, the user-facing lot label.
  - `batch_number` string — The user-facing lot / batch number label (e.g. `LOT-1241291`). Free-form and not required to be unique. Nullable; leave omitted or null if the batch has no external lot number.
  - `thc` string — A free-form THC label for the batch, as displayed in Distru (e.g. "18.5%"). This is a static value stored on the batch record; it does not set or derive from any lab result — the batch's primary test result tracks potency separately.
  - `cbd` string — A free-form CBD label for the batch, as displayed in Distru (e.g. "0.3%"). This is a static value stored on the batch record; it does not set or derive from any lab result — the batch's primary test result tracks potency separately.
  - `expiration_datetime` string — When the batch expires, as an ISO 8601 datetime (e.g. `2026-01-31T00:00:00Z`). Nullable; omit or send null if the batch has no expiration.
  - `harvest_datetime` string — When the batch's material was harvested, as an ISO 8601 datetime (e.g. `2025-09-15T00:00:00Z`). Nullable and optional; surfaced in the response as `harvest_datetime`.
  - `manufactured_datetime` string — When the batch was manufactured, as an ISO 8601 datetime (e.g. `2025-09-20T00:00:00Z`). If omitted on create, defaults to the time the batch is created. Surfaced in the response as `manufactured_datetime`.
  - `owner_id` string — The Distru user ID of the batch's designated owner. Must be an active user in your company that the authenticated user is allowed to assign to, otherwise the request is rejected. Nullable; omit or send null for no owner.
  - `description` string — Free-form notes about the batch. Nullable and optional.
  - `custom_data` object — A map of custom field IDs to their values. Use GET /public/v1/custom-fields?parent_object=batch to retrieve available custom fields, their IDs, and their types. The value format depends on the field's type: a text field takes a string, a date field takes a full ISO8601 datetime, and a checkbox field takes an array of its selected options.
  - `bin_ids` string[] — The IDs of the bins this batch is stored in. Behaviour: omit `bin_ids` to leave the batch's bins unchanged; pass `null` or an empty array to clear all bins; pass a non-empty array to replace the batch's bins with exactly those. Ignored unless bin inventory tracking is enabled for your company.

## Response `200`

A single batch

## Other responses

- `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/f8930b2c2924/schema)
