cannabis-label-new-york (v1)
The JobSchema for New York patient-specific dispensing labels — the label a registered organization's dispensing site affixes to each sealed medical-cannabis package it dispenses to a certified patient or designated caregiver. One job represents one dispensing transaction; the printer emits one label per unit of each line item (see Multiple line items).
It is intended for a label-capable printer loaded with die-cut adhesive label stock, not a receipt printer.
This page describes what the schema accepts and what the label prints. It is not legal advice — the registered organization remains responsible for compliance, including confirming that the data it sends is accurate and complete.
Working in more than one state? See cannabis-label-florida (v1) and cannabis-label-maryland (v1). The three labels carry deliberately different content and their payloads are not interchangeable — the differences are noted below.
Metadata
| Field | Value |
|---|---|
name | cannabis-label-new-york-v1 |
vertical | cannabis |
version | 1 |
source | apiV20 |
What this prints
Each job is a single dispensing transaction. Every printed label is self-contained: it repeats the shared transaction data (patient, caregiver, practitioner, dispensing site, date) and carries the details of exactly one product.
The label is text only. Unlike the Florida and Maryland labels it carries no fixed text of any kind — every row comes from the orderData you send. There is nothing the template supplies on its own, no warning paragraph, and no symbol or reserved corner. Two consequences worth planning for:
- Everything on the label is your data. A row is blank because you sent it blank; a row is missing because the field was optional and you omitted it.
- Plain stock. This label needs no pre-printed artwork, unlike cannabis-label-florida (v1), whose stock must carry the universal symbol.
Multiple line items
As in the other two states, this does not print one combined ticket — it prints one label per product package.
- Each entry in
line_items[]is one product. - An entry's
quantityis the number of units of that product, and one label prints per unit.quantity: 2produces two identical labels for that product. - The total number of labels printed is the sum of
quantityacross all line items. - Labels print in
line_itemsorder; within a line item, its N identical labels print consecutively. - The shared identity block (patient, caregiver, practitioner, dispensing site, date) is identical on every label; only the product block changes between products.
A single combined label covering several packages is never produced — each sealed package receives its own complete label.
quantity and quantity_dispensed are different thingsquantity is how many labels — the number of units of that product. quantity_dispensed is what prints in the QTY: row — the amount and unit of product in each package, e.g. 30 ml.
A line item with quantity: 2 and quantity_dispensed: { "amount": 0.5, "unit": "g" } prints two labels, each reading QTY: 0.5 g. It does not print one label reading 1 g.
How many labels print
A job with two line items:
| Line item | product_name | quantity | Labels printed |
|---|---|---|---|
| 1 | Hudson Valley Balance Tincture 30mL | 1 | 1 |
| 2 | Catskill Live Resin Concentrate 0.5g | 2 | 2 |
| Total | 3 |
Three labels come off the printer: one tincture label, then two identical concentrate labels. All three carry the same patient / caregiver / practitioner / dispensing-site / date block. This is the example payload below.
What the label carries
The rows below are what prints, in the order they appear on the label. Captions are printed in bold as shown.
| Row | Caption | Field | Notes |
|---|---|---|---|
| 1 | PATIENT: … ID: | patient.name, patient.registry_id | Both always print — see Registry identification numbers |
| 2 | CAREGIVER: … ID: | caregiver.name, caregiver.registry_id | Only row that can be absent — see The caregiver block |
| 3 | PRACTITIONER: | practitioner.name | Name only; nothing else about the practitioner prints |
| 4 | DISPENSED BY: | dispensing_site.name | |
| 5 | — | dispensing_site.address | Printed on its own row, no caption |
| 6 | — | dispensing_site.phone | Sent as +18455550142, printed as (845) 555-0142 — see Phone numbers |
| 7 | PRODUCT: | line_items[].product_name | Optional — omit it and no row prints |
| 8 | QTY: … DATE: | line_items[].quantity_dispensed, dispensed_date | {amount} {unit}; date prints MM/DD/YYYY |
| 9 | DOSING: | line_items[].dosing_instructions | Wraps rather than truncating |
| 10 | USE BY (OPENED): | line_items[].expiration_once_opened | Prints MM/DD/YYYY |
| 11 | PRACTITIONER NOTES: | line_items[].practitioner_recommendations | Always prints — send "None" when there is nothing to state |
Rows 1–6 are the shared identity block and are the same on every label of the job. Rows 7–11 come from the individual line item.
Registry identification numbers
This label prints a government identifier, and neither sibling label does. patient.registry_id — and caregiver.registry_id when a caregiver is present — appear on the printed label next to the corresponding name.
Two things follow from that:
- A name never prints without its number.
nameandregistry_idare both required on thepatientblock, and both required on thecaregiverblock when you send one. A partial block is rejected when the job is created — the request fails with a400and nothing prints, rather than printing a label carrying a name with no number. - Registry numbers are not sent to your webhook. See
orderDatais not sent to your webhook below — this is the field that makes that behaviour worth checking.
The number is printed verbatim, exactly as you send it. No format is imposed and none is validated.
The caregiver block
caregiver is the only optional block in the schema. Everything else at the root is required.
- Omit it and the label prints no caregiver row and no blank line — the rows below it move up. The label is shorter, not gappy.
- Send it and both the name and the registry identification number print.
- Sending a
caregiverwith only one ofname/registry_idis a400.
Formatting the data you send
The template prints the data you send largely verbatim. The rules worth knowing when populating orderData:
- Nothing is added to what you send. The label carries no fixed wording, so any wording restriction that applies to a product name applies to the string you send — the name is printed as given and is not screened for you.
product_nameis optional, and it is the only optional field on a line item. It exists so staff can tell which label belongs to which package when a transaction has several items. Omit it and no row prints.practitioner_recommendationsis required and always prints. When there is no recommendation to state, send the literal string"None". It prints verbatim, as an affirmative declaration on the label. That distinction matters:"None"states that there is nothing to add, whereas an absent field is a data gap — and omitting the field is a400, not a shorter label.dispensing_site.addressis a single line of text. Newlines are rejected — send"210 Main Street, Suite 4B, Poughkeepsie, NY 12601", not an address broken across lines. The label wraps it to the printed width for you.dispensing_site.phoneis sent in E.164 and printed in local form. See Phone numbers below — this is the one field where what you send is not what prints.- Units print verbatim, with no conversion.
quantity_dispensed.unitis your string:g,ml,capsules,units. - Amounts below 1 print with a leading zero —
0.5 g, never.5 g. Send the numeric value. - Dates.
dispensed_dateandexpiration_once_openedare ISO 8601 dates (YYYY-MM-DD) and print asMM/DD/YYYY. - English. Captions are in English. Product data you send is printed as supplied.
Phone numbers
dispensing_site.phone is the only field where what you send is not what prints.
Send it in E.164: a +1 country code followed by ten digits, e.g. "+18455550142". The label prints the local form — (845) 555-0142 — which is what a patient reads off the paper to dial.
| You send | +18455550142 |
| The label prints | (845) 555-0142 |
- North American numbers only. The value must match
+1followed by an area code starting2–9and nine further digits. Anything else — a bare(845) 555-0142, a number with spaces or dashes, a non-North-American number — is rejected with a400and nothing prints. - No extensions. E.164 has no extension concept, so a dispensary publishing a main line plus an extension should send the direct-dial number instead.
- The limit of 12 characters is exactly the length of a conforming number; it is not a field you can shorten or pad.
What this label does not include
New York splits the labelling a sealed medical-cannabis package must carry across three artefacts. This dispensing label is one of them, and printing another's content here would consume the space the seven required elements need. Do not send it:
| Artefact | Carries | Who applies it |
|---|---|---|
| The product label (Part 128, delegated by 113.12(k)(1)) | Potency, lot, unopened expiration date, storage conditions, processor identity | The manufacturer, before the package reaches the dispensary |
| The package safety insert (113.13(l)(12)) | The medicinal-use / pregnancy / driving warning block, and the original-container statement | The dispensing site, inside the package |
| This dispensing label (113.13(k)) | The patient-specific remainder — the seven elements on this page | The dispensing site, on the package |
So this label carries no warning block, no potency or lot number, no unopened expiration date and no symbol. Their absence is deliberate and is not an omission to correct by adding them to orderData — the schema rejects unknown properties, so a payload carrying them fails.
This also means printing a compliant label does not complete your packaging obligations. The insert and the product label are separate artefacts this print job does not produce.
Cannabis Law § 34(13) lists six elements a sealed package must carry. Two of them are allocated to no artefact by any current version of Part 113 or Part 128:
- the packaging date, and
- the dose count for inhalable products.
Neither is a dispensing-label element, so nothing on this label changes and there is nothing to send. This is a gap in the regulation, not in this schema — but it sits with the registered organization's packaging, so it is worth confirming how your own packaging addresses it. Verified against primary sources on 2026-08-12; see Sources.
Shape
orderData: object
├── id: string # required — dispensing-transaction identifier (minLength 1)
├── dispensed_date: string (date) # required — ISO 8601 date, e.g. "2026-08-03"
├── dispensing_site: { # required
│ name, # required — maxLength 60
│ address, # required — single line, maxLength 120
│ phone # required — E.164 "+1XXXXXXXXXX", maxLength 12
│ }
├── patient: { # required
│ name, # required — maxLength 48
│ registry_id # required — printed; maxLength 16
│ }
├── caregiver?: { # optional — the only optional block
│ name, # required when caregiver is sent — maxLength 48
│ registry_id # required when caregiver is sent — maxLength 16
│ }
├── practitioner: { name } # required — name maxLength 48
└── line_items: [NewYorkLabelItem, ...] # required — minItems 1
NewYorkLabelItem
{
quantity: integer (≥ 1), # required — one label prints per unit
product_name?: string, # optional — maxLength 80
dosing_instructions: string, # required — maxLength 160
quantity_dispensed: { # required
amount: number (> 0), # required
unit: string # required — e.g. "g", "ml", "capsules"
}, # unit maxLength 16
practitioner_recommendations: # required — send "None" if there are none
string, # maxLength 160
expiration_once_opened: # required — ISO 8601 date
string (date)
}
orderData is validated strictly — unknown/extra properties are rejected. Note that the root object uses dispensing_site and practitioner, where Florida uses mmtc / physician and Maryland uses dispensary / certifying_provider. A payload shaped for either of those states will not validate here.
Required fields
| Scope | Required fields |
|---|---|
orderData (root) | id, dispensed_date, dispensing_site, patient, practitioner, line_items |
dispensing_site | name, address, phone |
patient | name, registry_id |
caregiver (when sent) | name, registry_id |
practitioner | name |
each line_items[] entry | quantity, dosing_instructions, quantity_dispensed, practitioner_recommendations, expiration_once_opened |
quantity_dispensed | amount, unit |
caregiver is the only optional block, and product_name is the only optional field on a line item.
dispensed_date and expiration_once_opened must be ISO 8601 dates (YYYY-MM-DD). line_items must carry at least one entry.
webhook_url and webhook_method are also required on every job creation, but live at the root of the POST body (alongside printerId), not inside orderData. See the Create job page.
orderData is not sent to your webhook
The callback sent to webhook_url carries only a state update — the Job id, the state change, your externalId, and timestamps. No orderData is transmitted to your webhook URL.
On this schema that is worth stating plainly: the patient and caregiver blocks — including both registry_id values — are never sent to that endpoint. Retrieve them, if you need them, with an authenticated request to GET /v20/jobs/{jobId}.
Field length limits
Every string that prints on the label carries a length limit. A payload that exceeds one is rejected when the job is created — the request fails with a 400 and nothing prints. The response names the field that was too long.
The limits are set as a set, not field by field: they are chosen so that a label built from a payload respecting all of them fits the stock, with the longest permitted identity block and the longest permitted product text together.
| Payload path | Limit |
|---|---|
dispensing_site.name | 60 characters |
dispensing_site.address | 120 characters |
dispensing_site.phone | 12 characters — E.164, see Phone numbers |
patient.name | 48 characters |
patient.registry_id | 16 characters |
caregiver.name | 48 characters |
caregiver.registry_id | 16 characters |
practitioner.name | 48 characters |
line_items[].product_name | 80 characters |
line_items[].dosing_instructions | 160 characters |
line_items[].practitioner_recommendations | 160 characters |
line_items[].quantity_dispensed.unit | 16 characters |
Every one of these also has a minimum of 1 character — a limit never made a field optional. caregiver stays the only optional block and product_name the only optional field on a line item.
id is not length-limited; it is not printed on the label. dispensed_date and expiration_once_opened are constrained by date format rather than length. line_items must carry at least one entry and has no upper bound.
Content is never shortened to fit. Long text wraps, and a payload within every limit above prints complete. Nothing is truncated, and no label prints with a required row cut short — the length check happens at submission, before anything is sent to a printer.
Printed output
The default template produces a landscape label on 76.2 × 50.8 mm (3″ × 2″) die-cut stock — the same stock as cannabis-label-florida (v1), and not the square Maryland stock.
The preview below is one label — the tincture line item from the example, with a caregiver present. The same job also prints two identical concentrate labels (quantity: 2), for three labels total.

The thin outline marks the edge of the label stock. It is not printed. Content shorter than the label leaves the rest of the stock blank, as here.
Example
Two line items — quantities 1 and 2 — producing three labels. A caregiver is present, so every label carries the caregiver row. The second line item has no practitioner recommendation and sends the "None" sentinel.
{
"id": "NY-DISP-51284",
"dispensed_date": "2026-08-03",
"dispensing_site": {
"name": "Hudson Valley Dispensary",
"address": "210 Main Street, Suite 4B, Poughkeepsie, NY 12601",
"phone": "+18455550142"
},
"patient": {
"name": "Maria Delgado",
"registry_id": "P-1234567"
},
"caregiver": {
"name": "Luis Delgado",
"registry_id": "C-7654321"
},
"practitioner": {
"name": "Dr. Anne Chu, M.D."
},
"line_items": [
{
"quantity": 1,
"product_name": "Hudson Valley Balance Tincture 30mL",
"dosing_instructions": "Place 0.5 ml under the tongue twice daily; wait 2 hours before re-dosing.",
"quantity_dispensed": { "amount": 30, "unit": "ml" },
"practitioner_recommendations": "Do not exceed 2 doses in 24 hours.",
"expiration_once_opened": "2026-11-03"
},
{
"quantity": 2,
"product_name": "Catskill Live Resin Concentrate 0.5g",
"dosing_instructions": "Vaporize one 0.05 g dose; wait 15 minutes before repeating.",
"quantity_dispensed": { "amount": 0.5, "unit": "g" },
"practitioner_recommendations": "None",
"expiration_once_opened": "2026-10-15"
}
]
}
This orderData is the body of a POST /v20/jobs request (alongside printerId, webhook_url, and webhook_method). See Create job.
Sources
- N.Y. Cannabis Law § 34 — § 34(13) is the list a sealed package must carry
- 9 NYCRR Part 113 — final medical cannabis regulations (adopted, effective 27 March 2024) — the dispensing label is 113.13(k), the package safety insert 113.13(l)
- Adopted PLMA regulations, including 9 NYCRR Part 128 (effective 3 December 2025) — the manufacturer's product label, which 113.12(k)(1) delegates to
- New York State Department of State — 27 March 2024 rulemaking filing — the adoption notice for the Part 113 text above
Citations on this page were re-verified against these primary sources on 2026-08-13. Earlier drafts of New York material cited the pre-2024 lettering (113.13(i)); that numbering is superseded.