Documents
Intro
Documents controls the layouts your team prints — picking lists to pull stock, packing slips for the box, invoices, manifests, item labels, and the pickup slip. Once configured, they print from the Print menus on Orders and Shipments.
Purpose
- One consistent layout per document type for the whole store
- Include exactly the fields your bench and customers need — including custom fields and destination weather
Document types
| Type | Moment |
|---|---|
| Picking List / Order Pick List | Pulling stock for one shipment or order |
| Batch Pick List | Pulling stock for many shipments at once, grouped and sorted for the warehouse walk |
| Packing Slip | In the box; barcode source for Scan & Ship |
| Invoice | Customer or B2B paperwork |
| Returns Slip | Included for returns |
| Shipment Manifest | Carrier handoff at end of day |
| Item Label | One label per unit (per-quantity printing) |
| Local Pickup Slip | Counter handover — see Local Pickup |
| Custom Document | Anything else |
Every store starts with sensible defaults for each type; edit them or create additional documents.
Configure a document
- Open Advanced → Documents and create a document or open an existing one.
- Choose the type, then the page: A4, Letter, Legal, A5, A3, or Custom (inches); Portrait / Landscape; margins.
- Edit the Header, Body, Footer, and CSS tabs. Use Insert variable to place data — see Template variables. Turn on Tailwind CSS to use utility classes instead of writing CSS.
- In Printing, set the defaults the print preview starts from — see the table below.
- Watch the live preview: it renders with sample data until you pick one or more real shipments (or orders) in the preview picker, which is the quickest way to check bins, metafields and custom fields on your own data. Save, then print a test from a shipment.
From the Documents list each row offers Duplicate (copy layout and printing defaults under a new name — the starting point for one template per product group), Activate / Deactivate (inactive documents disappear from Print menus but keep their layout) and Delete; a star marks the Default document of each type, set in the editor.
Printing defaults
| Setting | What it does |
|---|---|
| Group items by | Consolidated lists only (Batch Pick List, Manifest): one row per SKU + bin, per SKU, or per SKU + warehouse location |
| Sort rows by | Default row order, e.g. Bin location so pickers walk the warehouse in order |
| Default location | Only print item rows stocked at this Shopify location. Empty = all locations |
| Default condition | A Condition applied whenever this document prints — e.g. Fish products. Empty = none |
| Per quantity | Item labels: one label per unit / pack instead of one page per shipment |
| Consolidate | One combined document for the whole selection instead of one page per shipment |
| Default | Preselected in the Print menu for its type |
Location and condition are defaults, not locks: the print preview preselects them and the operator can change them for one print without touching the document.
Templates use Mustache syntax. The short example below prints weather only when a forecast exists; the full syntax table is in Template syntax.
{{# weather }}Destination: {{ low }}–{{ high }}{{ unit }} · {{ condition }}{{/ weather }}
Template syntax (Mustache)
Documents are HTML plus Mustache tags. Nothing outside {{ … }} is evaluated. The editor's Insert variable and Helpers insert the same tags.
| Syntax | Name | What it does |
|---|---|---|
{{ name }} | Variable | Prints the value. Empty values print nothing (no error). HTML in the value is escaped. |
{{{ name }}} | Unescaped | Same as {{ name }} but allows HTML through — only use for trusted markup. |
{{# section }}…{{/ section }} | Section (if / loop) | If section is a list, repeats the block once per item (each item is the current context). If it is a truthy value (true, non-empty string, non-empty object), prints the block once. If missing / empty / false, prints nothing. |
{{^ section }}…{{/ section }} | Inverted section | The opposite of #: prints the block only when the value is missing, empty, or false — useful for fallbacks ({{^ sku }}-{{/ sku }}). |
{{> partial }} | Partial | Inserts a reusable snippet from the helper list (row_messages, head_messages, shopped_tag, shopped_service, bin_cell, …). |
{{# barcode }}…{{/ barcode }} | Helper | Renders the inner value as a Code 128 barcode SVG (what Scan & Ship reads). |
{{# qr }}…{{/ qr }} | Helper | Renders a QR code SVG. |
{{# money }}…{{/ money }} | Helper | Formats a number as money (12.5 → 12.50). |
Truthiness for sections. Use flags the renderer provides — has_discount, has_messages, has_shopped_rate, has_multiple_bins, shipment.local_pickup — rather than guessing. A missing Rate Shopper result makes has_shopped_rate false, so {{# shipment.has_shopped_rate }}…{{/ shipment.has_shopped_rate }} (and the shopped_* partials) stay blank.
Nested paths. Dot notation walks the context: {{ shipment.label }}, {{ shipping.postal_code }}, {{ metafields.binlocation }}, {{ custom_fields.item_bin_location }}. Inside a loop the current row is the context ({{ sku }} inside {{# items }}), and you can still reach parents ({{ shipment.label }}).
Common patterns
{{# items }}
{{ name }} · {{ sku }}
{{# has_discount }}was ${{ price }}{{/ has_discount }}
{{/ items }}
{{# shipment.has_shopped_rate }}
Shopped {{ shipment.shopped_label }} ${{ shipment.shopped_amount }}
{{/ shipment.has_shopped_rate }}
{{^ shipment.has_shopped_rate }}
<span class="muted">Not shopped</span>
{{/ shipment.has_shopped_rate }}
{{> shopped_tag }}
{{> shopped_service }}
Mustache has no <, >, =, or contains operators inside the template. Comparisons live in Conditions (document Default condition, print Filter rows by condition, and document messages). In the template you only show or hide blocks with # / ^.
Bin location on pick lists
{{ bin_location }} on an item row resolves in this order:
- A product or variant metafield synced for bin location (e.g.
custom.bin_location) - A line-item custom field whose name contains bin — e.g. Item Bin Location captured from
variant.metafield:custom.bin_location(see Custom Fields) - Otherwise
-
To print a specific custom field regardless of its name, use its slug: {{ custom_fields.item_bin_location }} (the field name lower-cased with underscores). Custom fields and metafields can also be sort columns (custom_fields.<slug>, metafields.<key>). The Shopify location id is never printed as a bin.
Bins per location. When Advanced → Locations maps a bin metafield to a location, {{ bin_location }} follows the Location chosen in the print dialog: a mapped location prints its own metafield (falling back to the default above), an unmapped one prints the default. With All locations, has_multiple_bins is true and the bins loop lists one entry per mapped location that has a value — location, location_short, key, value. The seeded pick lists already print {{# has_multiple_bins }}{{# bins }}{{ location_short }} {{ value }}{{/ bins }}{{/ has_multiple_bins }} and fall back to {{ bin_location }}; the Bin per location helper snippet inserts the same block. The AI assistant can make that edit too: it reads the template and saves the new HTML, and shows you the before and after before anything is saved.
Template variables
Insert variable lists everything available for the document's scope and drops it into the pane you are editing. The main groups:
| Group | Examples |
|---|---|
| Shipment / Order | shipment.label, shipment.full_label, shipment.delivery_date, shipment.shipby_date, shipment.staff_picked_by, shipment.notes_text, order.label, order.order_date, order.payment_status |
| Shopped rate (Rate Shopper) | See Shopped carrier & service |
| Customer, Shipping address, Billing address (order scope) | customer.full_name, shipping.address_1, shipping.postal_code, … |
| Items (loop) | {{# items }} … {{/ items }} with line_number, name, sku, barcode, quantity, price, discounted_price, line_discount, line_total, has_discount, vendor, type, image, location (fulfillment location name), location_short (the text before |, e.g. Online), locations_text, bin_location, has_multiple_bins + bins loop (per-location bins, see Locations), order_label (which order the line came from on merged shipments), metafields.<key>, custom_fields.<slug> |
| Item label (per-quantity documents) | item.name, item.sku, item.quantity, item.sequence ("2 of 3"), item.label_index / item.label_count, plus the same metafield and custom-field tokens |
| Supplies — consolidated (loop) | Batch Pick List / Manifest only: {{# supplies }} rows grouped per Group items by, with required (total units) and a nested {{# orders }} loop of label / quantity |
| Messages (loop) | has_messages and {{# messages }}{{ text }}{{/ messages }} with text, style, name — at the top level (shipment / order messages), inside items and supplies (per-line messages) and as item.messages on item labels; see Messages on documents |
| Merged orders | {{# all_orders }}{{ label }}{{/ all_orders }} lists every order on the shipment (or in the whole batch); merged_shipments, has_merges, and all_order_labels cover the merge lineage |
| Product metafield | Any synced custom.* key — type it or pick from the chips: {{ metafields.binlocation }} |
| Weather / Named addresses | Destination forecast and addresses.<key> blocks from the Weather app |
| Tags, Notes, Shipping methods, Units by product type | Loops over the shipment's tags, notes, shipping lines, and per-type unit counts |
| Store, Totals, Document | store.name, store.logo, totals.quantity, generated_at, count (shipments in the batch) |
Helpers wrap a value: {{# barcode }}{{ shipment.label }}{{/ barcode }} renders a Code 128 barcode (what Scan & Ship reads), {{# qr }}…{{/ qr }} a QR code, {{# money }}{{ price }}{{/ money }} a formatted amount. See Template syntax for #, ^, and >.
Shopped carrier & service (Rate Shopper)
When Rate Shopper has picked a winner for the shipment, the same values as the Shopped Carrier and Shopped Service columns on the Shipments table are available on the document. Nothing is invented at print time — if the column is a dash on the table, the tokens stay empty and the partials print nothing.
| Token | Meaning |
|---|---|
shipment.has_shopped_rate | true when a winner exists (use with {{# … }}) |
shipment.shopped_carrier | Display name — FedEx, UPS, USPS, DHL |
shipment.shopped_service | Human service name — FedEx Ground, UPS · 2nd Day Air |
shipment.shopped_label | Best single string for a pill or line (avoids FedEx · FedEx Ground) |
shipment.shopped_amount | Winner amount as 12.50 |
shipment.shopped_currency | Currency code when present |
| Partial | Prints | Seeded on |
|---|---|---|
{{> shopped_tag }} | Green supporting pill with shopped_label | Order Pick List, next to shipment tags |
{{> shopped_service }} | A line: Shopped label $amount | Order Pick List and Order Slip / packing-style headers, under Service |
Needs the Rate Shopper app on the plan. Custom templates: insert the helpers from the editor list, or ask the AI assistant to add them (it will preview the HTML change first).
Prices and discounts. price is the raw unit price, before any line discount. When a line is discounted, discounted_price is the unit price the customer actually pays, line_discount the amount taken off, and line_total the discounted quantity × price — the same figures the shipment page shows. has_discount lets a template strike through the original only when there is one:
{{# money }}{{ discounted_price }}{{/ money }}
{{# has_discount }}<s>{{# money }}{{ price }}{{/ money }}</s>{{/ has_discount }}
A 100% line discount prints 0.00, not the full price. Product thumbnails are cached and embedded when a PDF is generated, so a batch of a few hundred lines does not wait on the Shopify CDN.
Messages on documents
Advanced → Documents → Messages (Pro and Enterprise) prints a short note on a document when a Condition matches — "Add a heat pack" under every frozen line, "Fragile — double box" at the top of orders tagged fragile, "Pick from the cold room" next to items stocked at a given location. Nothing is stored on the order or shipment: messages are resolved every time a document renders, so changing a message or its condition changes the next print, including reprints of old shipments.
A message has:
| Field | Meaning |
|---|---|
| Condition | Any condition, active or not. Leave empty to always print. |
| Where | Per matching line (the condition's Product item rules decide which lines; shipment / order / customer rules gate the whole document) or Once per shipment / order (near the header when the shipment matches). Where the note sits on the page is decided by the template partial — see below. |
| Message | Up to 1000 characters. Tokens work the same way as in templates: {{ sku }}, {{ location }} and the other item tokens for per-line messages, {{ shipment.label }}, {{ order.label }}, {{ customer.full_name }} for both. |
| Style | Plain (default — just the text, same line as surrounding content) or a coloured pill: Info (blue), Warning (amber), Danger (red), Success (green). Use a pill when the note must stand out; keep Plain for quiet bench notes like "Back in bin location." |
| Documents | Limit the message to some documents; empty means every document. |
New messages start as drafts. Test on a shipment (per message, or for all of them from the toolbar) runs the real engine on a real shipment and shows which messages print at the top and on which lines — drafts included — then switch the message on. Messages print in list order; use Move up / Move down to change it.
Where they appear on the page. Templates print messages wherever they contain one of the partials. Partials are inline, so Plain text sits on the same line as the surrounding words (e.g. Orders: #158464 (qty: 4) Back in Bin Location.).
| Partial | Prints | Already in |
|---|---|---|
{{> row_messages }} | Per-line messages for that item / supplies row | Batch Pick List / Pick Slip: on the Orders: line next to the order numbers. Other seeded templates (picking list, packing slip, invoice, local pickup, manifest, order pick list): next to the item name |
{{> head_messages }} | Shipment / order messages, once, near the header | The seeded templates, next to the notes / ship-to block |
{{> item_messages }} | Per-line messages on an item label (item.messages) | Both seeded item labels |
{{# messages }}…{{/ messages }} | Your own markup with text, style and name | — |
Existing stores get the partials added (and Pick Slip / Batch Pick List relocated onto the Orders line) on upgrade when the row markup is the one that shipped; a heavily customised template keeps rendering unchanged until you add a partial yourself — pick Messages for an item / Orders line / Messages for the shipment / order from the editor's helper list, or ask the AI assistant to patch the template. Consolidated supplies rows on the Batch Pick List and Manifest show the union of the messages of the lines they combine.
Messages do not slow printing down: the list is loaded once per print, the conditions are evaluated in memory per shipment and per row, and a store with no messages pays one small query.
Conditions on messages use the same operators as everywhere else (=, !=, >, >=, <, <=, contains, …) — see Conditions → Operators. The template itself never evaluates those operators; the condition engine does, then the matching message text is printed.
Printing
Open Print on a shipment or order (or on a selection in a list) and pick a document. The preview opens with the document on the left and a panel on the right:
Page order for a selection. When you print several shipments at once (packing slips, order pick lists, Pack + Order, and similar one-page-per-shipment documents), the pages come out in the same order as the list you are looking at — whatever sort and filters you applied on Shipments. Sort the table the way you want to walk the warehouse, select the rows, then print.
| Control | Effect |
|---|---|
| Template | Switch to another active document without closing the preview |
| Sort rows by | Re-order item rows for this print |
| Location | Only rows stocked at that location; shipments with nothing left are skipped. All locations comes first, then your own locations (busiest first), then partner, drop-ship and holding locations |
| Filter rows by condition | Apply a Condition — see below |
| Opens your browser's print dialog for the rendered pages (turn off "Headers and footers" there) | |
| Download PDF | Generates a PDF with the same options |
| Open in new tab | The rendered pages in a browser tab — handy for a second screen or a printer kiosk |
The preview is the real rendered document, so Print is immediate; only Download PDF waits for PDF generation. Changes in the panel apply to this print only. The document's saved defaults (sort, location, condition) are preselected each time you open it; "Save a default on the document" means editing it under Advanced → Documents.
Filter rows by condition
Pick any active Condition. Two kinds of rule behave differently:
- Item rules (
Product itementity — type, SKU, vendor, tags, collections, location, metafields…) hide the item rows that do not match. A shipment left with no rows is skipped. - Shipment-level rules (
Order,Shipment,Customer,Shipping address, and the any item / no item gates) cannot be answered per row, so the whole condition is evaluated against each selected shipment and non-matching shipments are left out entirely — with all their rows.
A condition can mix both: order label starts with W and item type is Frozen prints only the frozen lines of wholesale orders. If nothing matches you get a single "No items match the selected filters" page instead of an unfiltered print.
Filters change what is printed only — hidden items still ship.
The "Printing" condition group
The dropdown lists every active Condition. To keep it short, create a rule group named Printing (any capitalisation) under Advanced → Conditions and move your print-only conditions into it: as soon as that group has one active condition, the print preview and the document editor list only that group. Stores without a Printing group keep seeing all conditions.
One template per product group
When a pick slip should look different for, say, fish and supplies, make two documents rather than one clever template:
- Duplicate the pick slip: Pick Slip – Fish and Pick Slip – Supplies.
- Give each its own layout, and set Default condition to Fish products / Supplies (and a Default location if the stock lives in one place).
- Both appear in Print and in the preview's Template switcher; choosing one gives the right layout with the right rows. The operator can still override the condition for a one-off print.
One row per fulfillment location
On a picking list, order pick list, batch pick list, Pick + Pack, and item labels, a line that Shopify has open at two or more fulfillment locations prints once per location, using that location's own quantity. A quantity of 3 with 1 at Online | Your Store and 2 at Retail | Your Store is two rows: Online required 1, Retail required 2. The pick-list column keeps the full location name. A line at only one location stays one row with the shipment quantity.
Packing slips, invoices, and the local pickup slip stay one line. Those go to the customer.
Item labels print the short name — the text before the | — under the shipment number ({{ item.location_short }}), including when the line is only at one location. In Split labels, two locations of the same product stay separate rows.
Item labels: split before printing
For Item Label (per-quantity) documents, Print… and Download PDF first open Split labels:
- The list shows only the lines with quantity above 1 that will print under the current Location / Filter rows by condition — quantity-1 lines have nothing to split, print one label each, and are counted in the total. When every line is quantity 1 the popup is skipped and the print goes straight through.
- Choose Print single label per line item or Break out every line item by quantity, or tick individual lines and set an optional pack per bag (20 snails, 5 per bag → 4 labels). The button shows the resulting label count.
- Confirm and the labels print (or download). After that Print acts directly; Split labels… reopens the popup to change the plan, Reset goes back to one label per line.
Carrier labels are not documents: their format (PDF / ZPL / EPL) and Zebra Browser Print setup live under Printing.
Print tracking
Print and Download PDF are recorded per shipment (previews and Open in new tab are not). Each print adds a Document printed entry to the shipment's Activities and immediately re-runs the shipment automation rules on the affected Awaiting shipments, so print-driven rules fire without waiting for the next sync. Order-scoped documents count for every Awaiting shipment holding that order's items.
Two shipment fields in Conditions read this history:
| Field | Meaning | Typical rule |
|---|---|---|
| Printed document | Names of documents printed or downloaded for the shipment | Printed document is empty → tag Not printed; Printed document contains Packing Slip → tag Slip printed |
| Printed document outdated by item changes | Printed documents whose last print is older than the last line-item change (item added or cancelled, quantity changed, lines split out or merged in) | … contains Packing Slip → add tag Reprint; the negation → remove Reprint. Reprinting re-runs the rules and clears the tag |
Address, customer, and shipping-method edits are not line-item changes and do not make a print outdated. A document that was never printed is never "outdated".
Who can use it
Documents (templates, print, PDF) are on every plan. Document messages are Pro and Enterprise — the Messages button and Advanced → Documents → Messages stay hidden on Basic. Shopped rate tokens need the Rate Shopper app (Pro+). Permissions View documents / Manage documents.
AI assistants (MCP) — with the Operations module:
| Tool | Role |
|---|---|
operations_list_documents / operations_get_document | List templates; read full HTML (check for {{> row_messages }}, {{> shopped_tag }}, …) |
operations_update_document | Edit any editor field; live changes need confirmation |
operations_list_document_messages / operations_test_document_messages | List messages; dry-run on a real shipment (drafts included) |
operations_create_document_message | Create as a draft (style defaults to Plain / neutral) |
operations_update_document_message / operations_delete_document_message | Edit, activate, or delete — activating or editing a live message needs confirmation |
operations_list_print_history | What was printed for a shipment |
orders_explain_rate_shop | Why Shopped Carrier / Service is filled or blank (feeds the same data documents print) |
Read storesboost://operations/semantics for the full Operations vocabulary.
Troubleshooting
| Symptom | Check |
|---|---|
| A condition in the preview "does nothing" | It has only shipment-level rules and every selected shipment matches — that is a pass, not a filter. Add a Product item rule to hide rows |
| Every shipment disappears when I pick a Location | No line item is assigned to that location. Item locations are the Shopify fulfillment location assigned to each line at import (fulfillment orders), so an order routed to another location has no rows there |
| Bin location prints a long number | Fixed in current versions — the Shopify location id is no longer used as a bin. Make sure a bin metafield or bin custom field exists |
| The bin is wrong for the warehouse I print for | Map each warehouse to its bin metafield under Locations and pick that Location in the print dialog |
| The Split labels list shows fewer lines than the shipment has | The current Location / condition filter hides them; clear the filters in the preview panel |
| A pick list puts the whole quantity on the first location | That line is open at more than one fulfillment location. Pick lists and item labels print one row per location, each with that location's quantity. Packing slips, invoices, and the local pickup slip stay one line |
| Condition dropdown is missing a condition | A Printing group exists and the condition is not in it; move it there or pick it as the document's default (defaults are always listed) |
| A message tests fine but does not print | The message is still a draft, is limited to other documents, or the template has no {{> row_messages }} / {{> head_messages }} partial — add it from the editor's helper list. A per-line message whose condition has no Product item rule prints under every line of a matching shipment |
| Shopped tag / line is blank | Rate Shopper has no winner for that shipment (Shopped columns are dashes on Shipments), or the template has no {{> shopped_tag }} / {{> shopped_service }} — add from Helpers. Confirm with orders_explain_rate_shop |
{{# weather }} block never appears | Destination weather is off or not fetched — see Weather |
Literal {{ sku }} prints on the page | The tag was put in an HTML attribute that Vue or the editor stripped, or a brace was missing — use Insert variable |
Related
- Conditions — operators (
=,>,contains, …), "Filter rows by condition", and the Printing group - Printing — label formats and printers
- Custom Fields — capture values you want printed
- Apps → Weather — forecast variables for slips
- Apps → Rate Shopper — Shopped Carrier / Service that documents can print
- Shipments → Labels & rates
- AI assistants — MCP tools for templates and messages