Units

Last updated on May 21, 2026

A unit is a physical object you test, identified by a unique serial and linked to a Part Revision. Each unit carries every Run you executed against it, typed metadata, Attachments, and any sub-unit assemblies.

TofuPilot uses "unit" as the canonical term. OpenHTF and other frameworks call this UUT or DUT.

Identity

A unit is described by a small set of fields, most of which you can set when you upload the first run.

FieldTypeRequiredNotes
serial_numberstringyes1-60 chars, ^[a-zA-Z0-9_.:+-]+$, unique per org (case-insensitive).
part_numberstringyesReferences an existing or auto-created Part.
revision_numberstringnoDefaults to A. Must exist on the part.
batch_numberstringnoLinks to a Batch.
parent_serial_numberstringnoParent unit when this unit is a sub-assembly.
sampleenumnogolden, failing, or null (production).

Lifecycle

You can create units three ways.

  • First Run: runs.create with an unknown serial creates the unit, part, revision, and batch (when a batch_number is sent) in one transaction. Auto-created parts get the name New Part.
  • API: units.create is the right entry point for legacy imports, pre-allocated serials, or setting reference samples before production.
  • Dashboard: create from Inventory → Units → New Unit.

Deleting a unit cascades to every run on that unit. Sub-units are not deleted, and their parent_serial_number is unset instead.

Reference samples

Reference samples let you stage known-good or known-faulty hardware without polluting your yield metrics.

ValueUseAnalytics
goldenKnown-good reference.Excluded from yield by default.
failingKnown-faulty reference.Excluded from yield by default.
nullProduction unit.Included.

You can toggle the Sample chip to include reference samples in any view.

Sub-units

Sub-units are units assembled into a parent, and each one keeps its own serial, runs, metadata, and attachments in a tree hierarchy.

  • A sub-unit has one parent at a time, and re-linking moves the unit. The API returns a warning instead of an error.
  • Cycles are rejected.
  • Deleting a parent unlinks its children, and the children themselves are not deleted.

Filter the units list with Exclude Sub-Units when you want to see only top-level assemblies.

Metadata

Metadata follows PATCH semantics, so omitted keys are preserved and null deletes a key.

  • Up to 50 keys per unit.
  • Keys: 1-40 chars, ^[a-zA-Z0-9_.:+-]+$.
  • Values: string (max 50000 chars), number, or boolean. The type is auto-detected.

Attachments

You can upload arbitrary files (calibration certificates, BOMs, photos) to a unit. Attachments are linked to the unit, not to a specific run.

attachments.py
client.units.attachments.upload(serial_number="SN-0001", file="calibration.pdf")
client.units.attachments.delete(serial_number="SN-0001", ids=[attachment_id])

In the dashboard

Open Inventory → Units to manage your units.

List columns: serial, part number, part name, revision, batch, last procedure, and last outcome. Rows are color-tinted by the last outcome.

Filters: part, revision (dependent on the selected parts), batch, sample, sub-unit toggle, created-by station, created-by user, procedure, run outcome, run count range, created-at range, run-date range, and latest-run-only toggle.

Actions: create a unit, edit the serial inline, assign a batch, set the sample type, edit metadata, add or remove parent and children, upload attachments, bulk delete, and export CSV (11 columns).

The unit detail page exposes the metadata editor, sub-unit tree, runs table, attachments, and audit activity.

Limits

A few hard limits apply to units.

  • Serial: 1-60 characters.
  • Metadata: 50 keys per unit, 40 chars per key, and 50000 chars per string value.
  • Sub-unit tree: no depth limit, with cycle prevention enforced.

API

The API exposes units.create, list, get, update, delete, add_child, and remove_child, plus attachment operations. See the REST API reference for schemas.

create_unit.py
from tofupilot.v2 import TofuPilot

client = TofuPilot()

client.units.create(
    serial_number="SN-0001",
    part_number="PCB-V1",
    revision_number="A",
    batch_number="BATCH-2026-04",
)

How is this guide?

On this page