Overview
Luminous HTML export templates use Mustache syntax ({{placeholder}}) to insert live data. This article covers every placeholder group available in the Purchase Order (supplier_purchase_order) and Production Run (production_run) template types, explains where the two templates differ, and shows the correct way to loop BOM components in a Production Run template.
You can always retrieve the authoritative, up-to-date placeholder list for any template type from Manage → Templates or via the API endpoint described at the bottom of this article.
Purchase Order HTML template placeholders
Order-level fields
| Placeholder | What it outputs |
|---|---|
{{purchaseOrderNumber}} | PO ID number |
{{purchaseOrderId}} | Same as above (alias) |
{{orderDate}} | Date/time the PO was created (also .date, .time, .dateTime, .dateShort) |
{{requestedShipDate}} | Requested ship date (also .formatted, .short, .long, .iso) |
{{arrivalDueDate}} | Expected arrival date (same sub-keys as above) |
{{notes}} | Public notes on the PO |
{{paymentTerms}} | Payment terms |
{{logisticLabel}} | Shipping method / carrier |
{{status}} | PO status |
{{alternateIds}} | Comma-separated alternate IDs |
Company information (company.*)
The company block is populated from your Ship-to Supplier PO Address setting, falling back to your general Company Address.
| Placeholder | What it outputs |
|---|---|
{{company.name}} | Your company name |
{{company.logoUrl}} | Company logo URL (use in <img src>) |
{{company.address.street1}} | Street line 1 |
{{company.address.street2}} | Street line 2 |
{{company.address.city}} | City |
{{company.address.state}} | State |
{{company.address.zip}} | ZIP / postal code |
{{company.address.country}} | Country |
{{company.address.fullAddressBlock}} | Full address as a formatted HTML block |
{{company.email}} | Accounting email |
{{company.website}} | Company website |
{{company.phone}} | Company phone |
Supplier contact (supplierContact.*)
Use the supplierContact block for supplier address and contact fields. The older {{#supplier.contacts}} loop and {{supplier.address.*}} fields still work but are deprecated.
| Placeholder | What it outputs |
|---|---|
{{supplierContact.name}} | Supplier name |
{{supplierContact.company}} | Supplier company |
{{supplierContact.email}} | Supplier email |
{{supplierContact.phone}} | Supplier phone |
{{supplierContact.street1}}, .street2, .city, .state, .zip, .country | Supplier address fields |
{{supplierContact.fullAddressBlock}} | Full supplier address block |
Ship-to contact (shipTo.*)
Same address field shape as supplierContact above, sourced from the PO's ship-to warehouse or custom address.
Order lines loop — {{#orderLines}}...{{/orderLines}}
Each row in the PO items table.
| Placeholder | What it outputs |
|---|---|
{{sku}} | Supplier / source SKU |
{{internalSku}} | Internal Luminous SKU |
{{itemNumber}} | Alias for internalSku |
{{description}} | Line description (falls back to SKU description) |
{{lineDescription}} | Line-specific description only |
{{skuDescription}} | Product description from the SKU |
{{productName}} | Product name |
{{quantity}}, .decimals0, .decimals2, .raw | Ordered quantity with formatting sub-keys |
{{unitCost}}, .currency, .raw | Cost per unit |
{{lineTotal}}, .currency, .raw | Total cost for this line |
{{uom}} | Ordered unit of measure (e.g. Pack) |
{{baseQuantity}} | Quantity in the base unit (e.g. ea) |
{{baseUnit}} | Base unit name |
{{uomConversions.<slug>}} | Quantity converted to a UOM keyed by slugified name (e.g. {{uomConversions.case}}, {{uomConversions.case-pack}}). Supports .decimals0, .decimals2, .decimalsMax4, .raw sub-keys. |
{{upc}} | UPC barcode |
{{downpaymentAmount}}, .currency | Downpayment amount for this line |
{{retailCost}}, .currency | Retail price |
{{skuCustomFields.*}} | SKU-level custom fields (dot-access by sanitised label) |
{{variantAttributes.*}} | Variant attributes (dot-access by attribute name) |
Additional fees loop — {{#additionalFees}}...{{/additionalFees}}
Covers any extra charges added to the PO — including fees inserted automatically by cost rules (such as tariffs or freight rules).
| Placeholder | What it outputs |
|---|---|
{{productName}} / {{description}} | Fee name (this is where a cost rule's name appears) |
{{quantity}} | Fee quantity |
{{unitPrice}}, .currency | Fee unit price |
{{lineTotal}}, .currency | Fee line total |
{{discountAmount}}, {{discountPercent}} | Discount applied to the fee |
{{downpaymentAmount}}, {{downpaymentPercent}} | Downpayment on the fee |
Cost rule name note: There is no dedicated
{{costRuleName}}placeholder. When a cost rule (e.g. a tariff rule) adds a fee to a PO, that fee appears in the{{#additionalFees}}loop, and its name is available via{{productName}}or{{description}}.
Financial totals
| Placeholder | What it outputs |
|---|---|
{{subTotal}}, .currency | Subtotal before shipping |
{{shippingTotal}}, .currency | Total shipping cost |
{{grandTotal}}, .currency | Grand total |
{{totalPaid}}, .currency | Amount already paid |
{{balancePayable}}, .currency | Remaining balance due |
{{totalUnits}} | Sum of all line quantities |
{{additionalFeesTotal}}, .currency | Sum of all additional fees |
Order-level custom fields
| Placeholder | What it outputs |
|---|---|
{{orderCustomFields.<FieldLabel>}} | Direct access to a PO custom field by sanitised label |
{{#orderCustomFields.list}} loop | Iterate over all PO custom fields (.label, .value) |
Production Run HTML template placeholders
Run-level fields
| Placeholder | What it outputs |
|---|---|
{{batchNumber}} | Production run / batch number |
{{batchId}} | Run ID |
{{batchName}} | Run name |
{{status}} | Run status |
{{orderDate}} | Date/time the run was created |
{{expectedCompletionDate}} | Expected completion date |
{{notes}} | Run notes |
{{alternateIds}} | Comma-separated alternate IDs |
Vendor & warehouse
| Placeholder | What it outputs |
|---|---|
{{vendorName}} | Co-manufacturer / vendor name |
{{vendorEmail}} | Vendor email |
{{vendorAddress}} | Vendor address (single formatted line) |
{{productionWarehouseName}} | Production warehouse name |
{{productionWarehouseAddress}} | Production warehouse address (single line) |
Summary block (summary.*)
| Placeholder | What it outputs |
|---|---|
{{summary.totalWorkOrders}} | Total work orders in the run |
{{summary.completedWorkOrders}} | Count with status "completed" |
{{summary.inProgressWorkOrders}} | Count with status "in_progress" |
{{summary.plannedWorkOrders}} | Count with status "planned" |
{{summary.draftWorkOrders}} | Count with status "draft" |
{{summary.progressPercentage}} | Overall progress % |
{{summary.totalPlannedQty}} | Total planned quantity across all work orders |
{{summary.totalCompletedQty}} | Total completed quantity |
{{summary.totalServiceCost}}, .currency | Total service cost |
Company information (company.*)
Same field shape as the PO template, but the address is sourced from your Ship-from / Invoice Address setting (falling back to Company Address), not the Ship-to Supplier PO Address.
| Placeholder | What it outputs |
|---|---|
{{company.name}} | Your company name |
{{company.logoUrl}} | Logo URL |
{{company.address.*}} | Address fields (same sub-keys as PO: street1, city, state, zip, country, fullAddressBlock) |
{{company.email}}, {{company.website}}, {{company.phone}} | Contact details |
Boolean presence flags (run level)
| Placeholder | When it is true |
|---|---|
{{hasConsolidatedBom}} | The run has at least one consolidated BOM row |
{{hasWorkOrders}} | The run has at least one work order |
{{hasWorkOrdersWithBomComponents}} | At least one work order in the run has BOM components |
Wrap an entire section in {{#hasConsolidatedBom}}...{{/hasConsolidatedBom}} (or the others) to hide it entirely when there is no data.
BOM components in a Production Run template — two loops
A Production Run template exposes BOM data in two distinct, complementary ways. Use whichever fits your layout.
Option A — Consolidated BOM (run level)
Use {{#consolidatedBom}} to show a single summary table that sums component requirements across all work orders in the run.
{{#hasConsolidatedBom}}
<table>
<tr><th>SKU</th><th>Name</th><th>Total Required</th><th>Available</th></tr>
{{#consolidatedBom}}
<tr>
<td>{{internalSku}}</td>
<td>{{productName}}</td>
<td>{{totalQty.formatted}}</td>
<td>{{availableQty.formatted}}</td>
</tr>
{{/consolidatedBom}}
</table>
{{/hasConsolidatedBom}}
Fields inside {{#consolidatedBom}}:
| Placeholder | What it outputs |
|---|---|
{{internalSku}} | Component SKU |
{{productName}} | Component name |
{{uom}} | Unit of measure |
{{totalQty}}, .formatted, .raw | Total required quantity across the run |
{{availableQty}}, .formatted, .raw | Current on-hand available quantity for this SKU |
{{workOrderCount}} | Number of work orders requiring this component |
{{workOrderNumbers}} | List of those work order numbers |
{{isSubAssembly}} | true if the component is itself an assembly |
{{skuCustomFields.<Label>}} | Component SKU custom fields |
{{variantAttributes.<Name>}} | Component variant attributes |
Option B — Per-work-order BOM (nested)
Use a nested {{#workOrders}} → {{#bomComponents}} loop to show each work order's BOM components separately.
{{#hasWorkOrdersWithBomComponents}}
{{#workOrders}}
<h3>Work Order {{workOrderNumber}} — {{producedSku}}</h3>
{{#hasBomComponents}}
<table>
<tr><th>Component SKU</th><th>Qty / Unit</th><th>Required</th><th>Available</th></tr>
{{#bomComponents}}
<tr>
<td>{{componentSku}}</td>
<td>{{qtyPerUnit.formatted}}</td>
<td>{{requiredQty.formatted}}</td>
<td>{{availableQty.formatted}}</td>
</tr>
{{/bomComponents}}
</table>
{{/hasBomComponents}}
{{/workOrders}}
{{/hasWorkOrdersWithBomComponents}}
Fields inside {{#workOrders}}{{#bomComponents}}:
| Placeholder | What it outputs |
|---|---|
{{componentSku}} | Component SKU |
{{componentDescription}} | Component description / product name |
{{supplyMode}} | Component supply mode |
{{qtyPerUnit}}, .formatted, .raw | Component quantity per unit of output |
{{requiredQty}}, .formatted, .raw | Total required quantity (qty per unit × planned qty) |
{{availableQty}}, .formatted, .raw | On-hand available quantity for this component SKU |
{{skuCustomFields.<Label>}} | Component SKU custom fields |
{{variantAttributes.<Name>}} | Component variant attributes |
Boolean {{hasBomComponents}} is set on every work order entry inside the {{#workOrders}} loop. Use it to hide the BOM table header when a particular work order has no components.
Summary: Use
{{#consolidatedBom}}for a single run-wide BOM table. Use{{#workOrders}}{{#bomComponents}}for a per-work-order breakdown. Do not look for abomComponentsloop at the top level of the Production Run template — it does not exist there.
Key differences between Purchase Order and Production Run templates
| Feature | Purchase Order | Production Run |
|---|---|---|
| Primary item loop | {{#orderLines}} | {{#workOrders}} (then {{#bomComponents}} inside) |
| BOM / component data | Not applicable | {{#consolidatedBom}} (run level) or {{#workOrders}}{{#bomComponents}} |
company.address source | Ship-to Supplier PO Address setting | Ship-from / Invoice Address setting |
| Order custom fields | orderCustomFields (PO-level) | orderCustomFields inside {{#workOrders}} (work order-level) |
| Financial totals | subTotal, grandTotal, totalPaid, etc. | summary.totalServiceCost only |
| Supplier/contact block | supplierContact.*, shipTo.* | vendorName, vendorEmail, vendorAddress |
| Cost rule name | Via {{#additionalFees}}{{productName}} | Not applicable |
company.email/website/phone | ✅ Present | ✅ Present |
Retrieve the live placeholder list
For any template type, call the placeholder API from your browser while logged in as an admin:
- Purchase Order:
GET /admin/manage/export-templates/purchase-order-html-placeholders - Production Run / Work Order: Navigate to Manage → Templates, open the Production Run or Work Order template editor, and use the placeholder browser panel — it queries the backend for your tenant's dynamic custom-field placeholders in addition to the static ones listed here.
The API response groups placeholders and includes any custom fields defined in your account, so it is the most complete reference.