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

PlaceholderWhat 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.

PlaceholderWhat 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.

PlaceholderWhat it outputs
{{supplierContact.name}}Supplier name
{{supplierContact.company}}Supplier company
{{supplierContact.email}}Supplier email
{{supplierContact.phone}}Supplier phone
{{supplierContact.street1}}, .street2, .city, .state, .zip, .countrySupplier 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.

PlaceholderWhat 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, .rawOrdered quantity with formatting sub-keys
{{unitCost}}, .currency, .rawCost per unit
{{lineTotal}}, .currency, .rawTotal 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}}, .currencyDownpayment amount for this line
{{retailCost}}, .currencyRetail 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).

PlaceholderWhat it outputs
{{productName}} / {{description}}Fee name (this is where a cost rule's name appears)
{{quantity}}Fee quantity
{{unitPrice}}, .currencyFee unit price
{{lineTotal}}, .currencyFee 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

PlaceholderWhat it outputs
{{subTotal}}, .currencySubtotal before shipping
{{shippingTotal}}, .currencyTotal shipping cost
{{grandTotal}}, .currencyGrand total
{{totalPaid}}, .currencyAmount already paid
{{balancePayable}}, .currencyRemaining balance due
{{totalUnits}}Sum of all line quantities
{{additionalFeesTotal}}, .currencySum of all additional fees

Order-level custom fields

PlaceholderWhat it outputs
{{orderCustomFields.<FieldLabel>}}Direct access to a PO custom field by sanitised label
{{#orderCustomFields.list}} loopIterate over all PO custom fields (.label, .value)

Production Run HTML template placeholders

Run-level fields

PlaceholderWhat 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

PlaceholderWhat 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.*)

PlaceholderWhat 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}}, .currencyTotal 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.

PlaceholderWhat 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)

PlaceholderWhen 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}}:

PlaceholderWhat it outputs
{{internalSku}}Component SKU
{{productName}}Component name
{{uom}}Unit of measure
{{totalQty}}, .formatted, .rawTotal required quantity across the run
{{availableQty}}, .formatted, .rawCurrent 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}}:

PlaceholderWhat it outputs
{{componentSku}}Component SKU
{{componentDescription}}Component description / product name
{{supplyMode}}Component supply mode
{{qtyPerUnit}}, .formatted, .rawComponent quantity per unit of output
{{requiredQty}}, .formatted, .rawTotal required quantity (qty per unit × planned qty)
{{availableQty}}, .formatted, .rawOn-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 a bomComponents loop at the top level of the Production Run template — it does not exist there.


Key differences between Purchase Order and Production Run templates

FeaturePurchase OrderProduction Run
Primary item loop{{#orderLines}}{{#workOrders}} (then {{#bomComponents}} inside)
BOM / component dataNot applicable{{#consolidatedBom}} (run level) or {{#workOrders}}{{#bomComponents}}
company.address sourceShip-to Supplier PO Address settingShip-from / Invoice Address setting
Order custom fieldsorderCustomFields (PO-level)orderCustomFields inside {{#workOrders}} (work order-level)
Financial totalssubTotal, grandTotal, totalPaid, etc.summary.totalServiceCost only
Supplier/contact blocksupplierContact.*, shipTo.*vendorName, vendorEmail, vendorAddress
Cost rule nameVia {{#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.