Templates & variables

A template is ordinary ZPL with variables in double braces: {{order.id}}, {{shipTo.name|John Doe}}. The Editor writes them, POST /v1/merge fills them with your data at print time (Print from your backend), and both <zpl-editor> and <zpl-viewer> fill them in the browser for previews.

^XA
^FO50,50^A0N,40,40^FD{{shipTo.name}}^FS
^FO50,110^A0N,30,30^FDQty {{qty|1}}^FS
^FO50,170^BY2^BCN,80,Y,N,N^FD{{order.id}}^FS
^XZ

Syntax#

SyntaxMeaning
{{name}}, {{ name }}The value of name. Whitespace inside the braces is ignored.
{{ship.address.city}}Nested objects: segments joined by .. A segment is an identifier (A-Z a-z 0-9 _ $ -, not starting with a digit or -) or a number.
{{items.0.sku}}A numeric segment indexes an array (0-based).
{{qty|1}}Default: the text after | up to }} (trimmed), used when the value is missing.

Values: strings are used as they are, numbers and booleans with String(v), and null becomes an empty string. An object or array as a value is an error.

Defaults are literal text: they cannot contain {, }, ^, ~ or control characters. In the Editor they are also the example value the canvas shows while you design.

Missing values#

A placeholder whose path is not in the data:

PlaceholderDefault mergeWith missing: "empty"
{{qty|1}} (has a default)11
{{qty}} (no default)error missing valueempty string

Errors are collected, not thrown one by one: the merge answers 422 merge_error with every problem in details: [{ record, placeholder, reason }] (record is 0-based, null for a problem in the template itself; up to 100 entries). It usually means the template and your data disagree about a field name.

Arrays: one label per record#

data may be one object or an array of objects. An array produces one copy of the template per record, joined by a newline, in order, and the response header X-Record-Count says how many. That is how you print a batch (every carton of a shipment) with one call. In the browser, <zpl-viewer> shows a pager for them, and the Editor's sample data can be an array too (the canvas has a record switcher).

A template that changes the ZPL control characters (^CC, ^CT, ^CD) can only be merged with one record unless it restores them at the end.

Escaping: why data cannot inject ZPL#

Values from your data are escaped for the exact place they land in, so a value can never start a ZPL command, whatever it contains (a customer name with ^XZ in it prints as text):

Where the placeholder isWhat happens to special characters (^, ~, the current prefixes, control characters)
Field data: ^FD, ^FV, ^SNThey become ^FH hex escapes (^ → _5E). If the field has no ^FH, the merge inserts one, and from there on a literal _ of the template is escaped as _5F.
A comment: ^FXThey become _XX text (no ^FH is inserted, it would change the next field).
A command parameter: ^FO{{x}},{{y}}, ^BY{{w}}, ^PQ{{n}}Nothing is escaped: the value must match ^[A-Za-z0-9 .:_+\-/=!@]*$ (and contain no current prefix or delimiter), else the merge fails with unsafe value for parameter.
curl -s -X POST "https://labelviewer.example.internal/v1/merge" -H 'Content-Type: application/json' -d '{
  "template": "^XA^FO50,50^A0N,40,40^FD{{name}}^FS^XZ",
  "data": [{"name": "Alpha"}, {"name": "Gamma ^XZ"}]
}'
^XA^FO50,50^A0N,40,40^FDAlpha^FS^XZ
^XA^FO50,50^A0N,40,40^FH^FDGamma _5EXZ^FS^XZ

Template authors, on the other hand, can write any ZPL. Treat editing templates as a privileged action in your app.

Where placeholders are not allowed#

The merge rejects the template (a details entry with record: null) for a placeholder:

  • inside a command name (^{{cmd}}), or in ^CC, ^CT, ^CD or ^FH;
  • in a binary payload (^GFB/^GFC, binary ~DY): templates are text, use ASCII hex or :Z64: graphics;
  • in a ^GF or ~DY byte-count header. In the data of a ^GFA whose header is literal they are fine.

As a backstop, every merged copy must have exactly the template's sequence of commands, or the merge fails.

Sample data#

Each template can carry sample record(s), loaded with load({ sampleData }) and edited in the Editor's Sample data tab. They feed Preview with sample data, the Variables tab (every variable with its default, the fields that use it and the current record's value), Export merged and Test with API. Keep sample data small and free of real customer data: it is shown to everyone who opens the template. Real data comes from your orders at print time.

Starter samples#

New from sample… in the Editor offers generic starting points: GS1-128 SSCC carton and pallet labels and several carton-label variants, 4 x 6 in at 203 dpi, each with matching sample data.

Not verified, no compliance guarantee. The starter samples follow the public GS1-128 shipping container label layout. They are not based on, endorsed by or verified against any retailer's routing guide, and using one does not make a label compliant. Verify every field, size and position against the current routing guide of the retailer you ship to, and complete that retailer's label testing or certification before shipping. Every sample template starts with this notice as a ^FX comment.

You can replace or extend the gallery with your own templates through the samples property (or hide it with features="no-samples"), see the Reference. If you ship templates for a particular retailer, claim compliance only if you have verified them.