GuidesMETRC field guides for Rhode Island
METRC item UnitWeight in Rhode Island: it persists only when the category has RequiresUnitWeight true, and the PUT returns 200 either way
On 2026-07-09, on the Rhode Island METRC sandbox, PUT /items/v2/ answered 200 {"Ids":[N],"Warnings":null} and silently dropped UnitWeight on all 4 tested categories whose RequiresUnitWeight flag is false (Bud/Flower, Kief, Concentrate, Concentrate (each)), and persisted it on all 3 tested categories whose flag is true (Shake/Trim, Infused Pre-Rolls, Pre-Packaged Bud). The flag is a per-category boolean on GET /items/v2/categories, the PUT is otherwise live (a Name change on the same call persists), and production agreed in July 2026. This is neither a sandbox artifact nor a bug; it is how the category flag works.
- Observed
- Revised
- Environment
- Rhode Island production and sandbox
What this page is
This page records what SeedLink’s METRC integration observed on the Rhode Island API when it wrote item unit weights and read them back, dated, with the call that produced each observation. It is not METRC documentation and it is not regulatory guidance: it describes how the item endpoints behaved for licensed Rhode Island cultivators on the dates given.
It is not a guide to choosing a unit of measure for a product, and the matrix here is not the whole category table: it covers the 7 categories that were tested, out of the 21 Rhode Island served on 2026-07-28. Other states run their own METRC instances and can differ on every point below.
Where SeedLink’s own handling is described (a per-unit weight kept on its side of the integration, the item-creation cascade), it is described as what the code does today, not as the only correct design.
How every number on this site is observed, dated and bounded is on the methodology page, which also lists what SeedLink does not publish.
The flag lives on the categories endpoint, not on the item
GET /items/v2/categories?licenseNumber=... returns one row per item category the state serves, and each row carries a boolean named RequiresUnitWeight. That boolean, not anything on the item itself, decides whether the UnitWeight you send on POST /items/v2 or PUT /items/v2/ is kept. The same rows carry other per-category flags (RequiresStrain among them; see the strain guide linked below).
On 2026-07-09 SeedLink live-tested 7 categories on the Rhode Island sandbox at one cultivator facility, 3 with the flag true and 4 with it false, sending UnitWeight on both the create and the update call and reading each item back afterwards.
| Category | `RequiresUnitWeight` | What `PUT /items/v2/` and `POST /items/v2` did with `UnitWeight` |
|---|---|---|
| Shake/Trim | true | Persisted; POST returned HTTP 400 without it |
| Infused Pre-Rolls | true | Persisted; POST returned HTTP 400 without it |
| Pre-Packaged Bud | true | Persisted; POST returned HTTP 400 without it |
| Bud/Flower | false | HTTP 200, silently dropped |
| Kief | false | HTTP 200, silently dropped |
| Concentrate | false | HTTP 200, silently dropped |
| Concentrate (each) | false | HTTP 200, silently dropped |
Rhode Island served 21 item categories when SeedLink read the endpoint live at one production cultivator facility on 2026-07-28. The 7 above are the ones the matrix exercised; the flag on the other categories is read from the same endpoint, never inferred from a category name.
The 200 is real, and it is not a sandbox bug
The update call answers 200 {"Ids":[N],"Warnings":null} in both halves of the matrix: no warning, no partial-success code, nothing in the body that says a field was ignored.
The call is not a no-op either. On the same PUT /items/v2/ that dropped UnitWeight on a flag-false category, a Name change persisted. The endpoint is live: it applies the fields the category allows, discards the one it does not, and reports success for the call as a whole.
SeedLink’s own field guide once recorded this as a Rhode Island sandbox no-op bug. It is neither sandbox-specific nor a bug. A read-only pass over production packages in July 2026 found that the only weight-based production packages carrying a unit weight belonged to the one concentrate category that carries the flag, Terpenes/Non Psychoactive Derivatives, and that the weight had been set at item creation. Production and sandbox agree on the rule.
A 200 on PUT /items/v2/ means the call was accepted, not that every field in it was stored. Read the item back before believing a unit weight.
A null unit weight can never be repaired by writing it back
The consequence follows from the flag alone, in three steps, and it leaves no package for which writing UnitWeight back to METRC is both needed and effective.
- Flag true:
POST /items/v2returns HTTP 400 withoutUnitWeight, so the value is required at creation, never null, and never needs writing. - Flag false:
UnitWeightis dropped on every write, so writing it changes nothing. - A package with a null unit weight is in a flag-false category by construction, so the write that would fix it is the write that is dropped.
SeedLink therefore stores the per-unit weight on its own side for a mass package the cultivator sells as fixed units, and re-applies it to the row’s weight and unit count on every sync. METRC keeps the package in grams; the fixed-unit view is re-derived on each pass rather than read back.
SeedLink’s packaging pre-flight used to send the write anyway; since 2026-08-23 it writes nothing to METRC. The write only ran for packages whose unit weight was null, which are flag-false by construction, so it was dropped; on the flag-true categories where it would have persisted, the value was never null, so it was never sent; and nothing in the packaging flow reads the parent item’s UnitWeight, since every per-unit weight comes from SeedLink’s own row. Measured on a replica of one production catalog before the change, it logged "updated" and then "did not persist" on every run. Its only possible effect was to abort a run on a 400 or 401 over a value nothing read.
| Write | `RequiresUnitWeight` | Status | What persisted |
|---|---|---|---|
POST /items/v2 without UnitWeight | true | HTTP 400 | Nothing; the item is not created |
POST /items/v2 with UnitWeight | true | Item created; code not recorded | The item and its UnitWeight (the 7 g item in the cascade test, July 2026) |
PUT /items/v2/ with UnitWeight | true | HTTP 200 | UnitWeight |
PUT /items/v2/ with UnitWeight | false | HTTP 200, body {"Ids":[N],"Warnings":null} | Every field except UnitWeight |
PUT /items/v2/ with a Name change | false | HTTP 200 | The new Name, on the same call that dropped UnitWeight |
PUT /items/v2/ from a facility that does not own the item | either | HTTP 401, 404, or 400 with Item N does not exist at the current Facility. | Nothing |
The SeedLink pre-flight UnitWeight write, before 2026-08-23 | false by construction | HTTP 200 | Nothing: logged "updated", then "did not persist", every run on a production replica |
Items belong to the facility that created them
An item is owned by the facility that created it, and a package transferred in from another facility (a lab’s returned packages, for example) still references the original item by Item.Id. GET /items/v2/{id} treats licenseNumber as optional and returns the item across facilities. PUT /items/v2/ does require licenseNumber, and only the owning facility accepts the call.
A facility that does not own the item answers in one of three shapes: HTTP 401, HTTP 404, or HTTP 400 with the body Item N does not exist at the current Facility. All three mean the same thing: try the next facility the credential can see. The 400 shape was observed live on the Rhode Island sandbox on 2026-07-09, and it mattered. SeedLink’s facility walk had retried only on 401 and 404 and aborted on the 400, so an item genuinely owned by another facility never reached its owner. The walk now treats all three as a wrong facility and continues down /facilities/v2/. The general lesson: a 400 on this endpoint is not a validation failure until its body has been read.
How to mint an item without guessing a category
Every retail size is a distinct METRC item that has to exist before a package can be created against it, and new strains and new sizes arrive constantly. The wrong shortcut is to guess. The ItemCategory prints on the physical sticker, and METRC locks the sticker’s item name 24 hours after packaging, so a guessed category is a re-label. SeedLink resolves the item through a tightening cascade, and every create is confirmed by the cultivator in a modal before the irreversible call.
| Step | What it reads | What it guarantees |
|---|---|---|
| Reuse | An existing Each item matching the strain and the size | No create at all; a re-run finds the same item |
| Same-size sibling | A same-size sibling item’s exact name and its real ProductCategoryName | The category is copied from a real item, never guessed; the name follows the sibling with the strain swapped |
| Learned template | The dominant "{strain} {size}" name skeleton in the brand’s own item history, with at least 2 items sharing it | A size the brand has never shipped gets a name in the brand’s own convention, and the brand’s most common flower Each category |
| Plain or bulk | No Each items at all, or no skeleton shared by 2 items | A plain "{strain} {size}" name, or the line ships as bulk grams |
Live-verified on the Rhode Island sandbox in July 2026: a 7 g quarter of a strain that had only 3.5 g items had no same-size sibling, so the learned template created a "<strain> - 7g" item as Each, Pre-Packaged Bud, UnitWeight 7 Grams. A second run reused that item rather than minting another. Pre-Packaged Bud is a flag-true category, so the unit weight was required at creation and persisted.
UnitWeightUnitOfMeasureis hardcoded toGramson every create path. A unit-of-measure mismatch on that field multiplied a weight by 1,000 in one live test in June 2026, so the unit is pinned rather than passed through.ItemCategoryis always copied from a real sibling item or from the brand’s most common flowerEachcategory, never inferred from a keyword, because it prints on the sticker.- Every create path finds by exact name first, so a retry reuses and never double-mints.
- Auto-creation is scoped to flower with a known strain; concentrates carry a null
StrainNameand are reuse-only.
The unit weight is not the draw
Two different quantities are easy to conflate. UnitWeight describes one unit of the item. The ingredient draw on POST /packages/v2/ describes how much of the source package moves into the new one, and it has to be expressed in the source package’s own unit of measure, read live from its UnitOfMeasureName rather than assumed from the category. METRC rejects a draw in grams from an Each source. A mass source gives up total grams; a count source gives up base units, the package quantity times the pack count.
A multi-pack is a distinct item from its single, not a quantity of it: same category and strain, still Each, with UnitWeight equal to the pack count times the per-unit grams and a "<single> - N pack" name. As a worked example, a 3-pack of 1.2 g units carries UnitWeight 3.6. The shape matches what one Rhode Island production facility already held for the multi-packs it makes by hand. The multi-pack package is drawn from the singles package as base units, arithmetic METRC does not do for you.
The category matters here for a second reason: in SeedLink the mapped master category drives the unit-of-measure choice for packaging, so a mis-mapped category does not mislabel a menu, it blocks packaging with an error naming the category. A blocked run costs a support message; a wrong category on a sticker costs a re-label.
Check it yourself: one read answers it, one write confirms it
The flag is readable with one GET and no writes. The confirmation needs a write, so do it on a sandbox item you own and send only the field you are testing, because the PUT is live.
GET https://api-ri.metrc.com/items/v2/categories?licenseNumber=<your license>
Authorization: Basic <base64 of VENDOR_KEY:USER_KEY>
for each row in the response:
note Name and RequiresUnitWeight
then, for one item you own in a category whose flag is false:
GET /items/v2/<item id> record UnitWeight
PUT /items/v2/?licenseNumber=<your license> the item, UnitWeight set, UnitWeightUnitOfMeasure "Grams"
GET /items/v2/<item id> compare UnitWeight to what you sentIf the read-back matches what you sent, the category keeps unit weights. If it comes back unchanged beside a 200, you have reproduced this page, and the value belongs on your side of the integration.
Questions
- Is the dropped UnitWeight a Rhode Island sandbox bug?
- No. The matrix was run on the sandbox on 2026-07-09, and a read-only pass over production packages in July 2026 agreed: the only weight-based production packages carrying a unit weight sat in
Terpenes/Non Psychoactive Derivatives, the one concentrate category withRequiresUnitWeighttrue, and the weight was set at item creation. - Why does PUT /items/v2/ return 200 if it ignored my UnitWeight?
- Because the call as a whole succeeded. The endpoint applies the fields the category allows and discards
UnitWeighton a flag-false category; aNamechange on the same call persisted. The body is{"Ids":[N],"Warnings":null}either way, so only a read-back shows the difference. - Which categories keep UnitWeight?
- The ones whose row on
GET /items/v2/categoriescarriesRequiresUnitWeight: true. Of the 7 categories tested on 2026-07-09, 3 keep it (Shake/Trim, Infused Pre-Rolls, Pre-Packaged Bud) and 4 drop it (Bud/Flower, Kief, Concentrate, Concentrate (each)). Rhode Island served 21 categories on 2026-07-28; read the flag for the rest rather than inferring it from the name. - Can I make a bulk flower package sellable as fixed units by writing UnitWeight to METRC?
- No. A package with a null unit weight is in a flag-false category by construction, and that is exactly where the write is dropped. Keep the per-unit weight on your own side and re-apply it after every sync, which is what SeedLink does; its packaging pre-flight has written nothing to METRC since 2026-08-23.
- Why does my PUT fail with "Item N does not exist at the current Facility."?
- Because the license on the call is not the facility that created the item. Items are facility-scoped: only the owner accepts
PUT /items/v2/, and a non-owner answers HTTP 401, 404, or that 400. Read withGET /items/v2/{id}, which works across facilities, and write from the owning license. - What happens if UnitWeightUnitOfMeasure is not Grams?
- A unit-of-measure mismatch on that field multiplied a unit weight by 1,000 in one live test in June 2026. SeedLink hardcodes the field to
Gramson every create path rather than passing a unit through. - How do I create an item for a size I have never sold without guessing the category?
- Copy it from a real item: reuse an existing
Eachitem for the strain and size, else copy a same-size sibling’s exact name andProductCategoryName, else render the brand’s own dominant name skeleton (at least 2 items must share it) at the new size with the most common flowerEachcategory. On the sandbox in July 2026 this created a "<strain> - 7g" item at 7 Grams for a strain that had only 3.5 g items, and a re-run reused it. - Does the parent item’s UnitWeight matter when packaging?
- Not in SeedLink’s packaging flow: nothing reads it, and every per-unit weight comes from SeedLink’s own row. What does matter is the source package’s unit of measure, read live from
UnitOfMeasureName, because a draw in grams from anEachsource is rejected by METRC.
Sources and how to cite this page
- Live-tested matrix of PUT /items/v2/ and POST /items/v2 against the RequiresUnitWeight flag on GET /items/v2/categories, Rhode Island sandbox, one cultivator facility, 2026-07-09; the wrong-facility 400 body observed the same day.
- Read-only read of weight-based active packages carrying a unit weight at Rhode Island production cultivator facilities, July 2026.
- GET /items/v2/categories at one Rhode Island production cultivator facility, read live, 2026-07-28; the table is pinned by a test in the SeedLink repository, so the page cannot drift from the code.
- SeedLink item-creation cascade and multi-pack item flow, live-verified on the Rhode Island sandbox, July 2026; the packaging pre-flight change of 2026-08-23; the unit-of-measure guard from a live test in June 2026.
- SeedLink METRC field guide (docs/METRC.md), sections UnitWeight persists ONLY for RequiresUnitWeight=true categories, Items Are Facility-Scoped, Tier A Category-Aware UoM, and Categories.
SeedLink, "METRC item UnitWeight in Rhode Island: it persists only when the category has RequiresUnitWeight true, and the PUT returns 200 either way", observed 2026-08-23, revised 2026-09-20, https://www.seedlink.net/guides/metrc/unit-weight-requires-unit-weight
Every figure above carries the date it was observed and the environment it was observed in. A count belongs to one facility on one day; the field names, orderings and error messages are the durable part.
Machine-readable copy of this page, with the same facts, dates and sources: /guides/metrc/unit-weight-requires-unit-weight/data.json
Related
- METRC’s StrainName is an ingredient, not the product name: Rhode Island requires a strain on infused items, so the two fields diverge by design
- METRC package adjustments in Rhode Island: Quantity is the new total, not a delta, and the adjustments log reports the difference
- Combining two METRC packages of one item in Rhode Island is one POST with two ingredients, and the drained sources stay active until you finish them
- METRC package tags in Rhode Island: adult-use and medical share one list, and medical comes first
- All metrc field guides for rhode island
- Methodology: what SeedLink publishes and what it does not