TestResult

Upsert a test result

Create or update a single test result (lab result / Certificate of Analysis) for one package or batch. Omit id to create; pass an existing test result's id to update it. Same URL and request shape for both.

On create, attach the result to exactly one material by sending either package_id or batch_id (never both, and at least one is required). On update, send neither — the attachment is fixed once created, and passing package_id or batch_id on an update is rejected. Only non-compliance-tracked test results can be updated through this endpoint; results synced from Metrc or BioTrack are read-only here and any update attempt is rejected.

Updates are sparse: only the fields you send are changed, and any field you omit keeps its stored value. To clear an optional potency or lab field back to null, send it explicitly as null. Potency percentage fields accept at most 4 decimal places and must be between 0 and 100.

Results created or updated here are Distru-only records — this endpoint never pushes to Metrc or BioTrack. Compliance-tracked results only ever arrive by syncing from those systems, which is why they are read-only here.

Effects beyond the test result row: • Creating a result attaches it to the given package or batch and propagates it down to that material's child packages where applicable. • is_primary: true makes this the primary result for its package/batch: it unsets whatever result was previously primary there, propagates to child packages, and repoints those materials' inventory/stock records at this result (so it becomes the potency shown for that inventory). A material always keeps exactly one primary — the first result created for a material becomes primary automatically even if you send is_primary: false. • You cannot flip an existing primary result from is_primary: true to false directly; instead mark a different result on the same package/batch as primary, which demotes this one automatically.

Required permission: products_permissions_edit.

post/public/v1/test-results

Request body

idstring

The test result's ID. Omit to create a new result; include an existing result's ID to update it (an ID that doesn't exist for your company returns 404). Only non-compliance-tracked results can be updated — updating one that is synced from Metrc or BioTrack is rejected. When present, package_id and batch_id must be omitted.

package_idstring

The ID of the package this result is attached to. Provide exactly one of package_id or batch_id on create; sending both, or neither, is rejected. Must be omitted on update (the attachment cannot be moved).

batch_idstring

The ID of the batch this result is attached to. Provide exactly one of package_id or batch_id on create; sending both, or neither, is rejected. Must be omitted on update. Batch-attached results cannot be compliance-tracked.

additional_test_resultsobject

Required on create; on update, omit to leave the stored object unchanged. A key/value object of extra lab fields beyond the built-in THC/CBD ones (e.g. terpenes, pesticides, moisture). Which keys are valid depends on the attached material's product-category test result settings; an unrecognized key is rejected, and keys valid overall but outside that material's configured set are dropped from responses. Each value must be a valid decimal within that field's configured decimal-place, digit, and range limits (percentage fields 0–100). Null values are ignored. When you do send it on update it replaces the entire object, not merged per-key — include every entry you want to keep; send {} to clear them all. See here for the valid options.

mg_per_unit_type'mg/g' | 'mg/mL'

Required on create; on update, omit to leave unchanged. The unit the *_mg_per_unit fields are expressed in. One of mg/g or mg/mL.

is_primaryboolean

Required on create; on update, omit to leave unchanged. Whether this is the primary result for its package/batch. Setting true makes it primary — it demotes any other primary on the same material, propagates to child packages, and repoints that material's inventory/stock potency at this result. You cannot change an existing primary result from true to false directly; mark a different result on the same package/batch as primary instead, which demotes this one automatically. Note: the first result created for a material always becomes primary regardless of this flag.

release_datestring

The lab's release date for this result, as an ISO8601 date (YYYY-MM-DD). Optional; omitting it on update leaves it unchanged. Send null to clear it.

lab_license_numberstring

The license number of the testing lab. Optional; omitting it on update leaves it unchanged. Send null to clear it.

lab_namestring

The name of the testing lab. Optional; omitting it on update leaves it unchanged. Send null to clear it.

namestring

Required on create; on update, omit to leave unchanged. A display name for this result (250 characters or fewer).

thc_percentagenumber

THC as a percentage of the material (0–100, at most 4 decimal places). Optional; omitting it on update leaves it unchanged. Send null to clear it.

total_thc_percentagenumber

Total THC as a percentage of the material, accounting for THCA conversion (0–100, at most 4 decimal places). Optional; omitting it on update leaves it unchanged. Send null to clear it.

thc_mg_per_unitnumber

THC content per unit, expressed in the unit named by mg_per_unit_type. Must be 0 or greater; unlike the percentage fields it has no upper bound and no decimal-place limit. Optional; omitting it on update leaves it unchanged. Send null to clear it.

total_thc_mg_per_unitnumber

Total THC content per unit (accounting for THCA conversion), expressed in the unit named by mg_per_unit_type. Must be 0 or greater; no upper bound and no decimal-place limit. Optional; omitting it on update leaves it unchanged. Send null to clear it.

cbd_percentagenumber

CBD as a percentage of the material (0–100, at most 4 decimal places). Optional; omitting it on update leaves it unchanged. Send null to clear it.

total_cbd_percentagenumber

Total CBD as a percentage of the material, accounting for CBDA conversion (0–100, at most 4 decimal places). Optional; omitting it on update leaves it unchanged. Send null to clear it.

cbd_mg_per_unitnumber

CBD content per unit, expressed in the unit named by mg_per_unit_type. Must be 0 or greater; unlike the percentage fields it has no upper bound and no decimal-place limit. Optional; omitting it on update leaves it unchanged. Send null to clear it.

total_cbd_mg_per_unitnumber

Total CBD content per unit (accounting for CBDA conversion), expressed in the unit named by mg_per_unit_type. Must be 0 or greater; no upper bound and no decimal-place limit. Optional; omitting it on update leaves it unchanged. Send null to clear it.

Response

A single test result

Changes

No recorded changes to this endpoint across all 1 revision of this API.