> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alforse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Printing & labels

> Printer routing, ZPL and PDF labels, and the print-job queue that turns a receipt or a finished job into a label on a shelf.

Alforse can print a physical label automatically when you post a receipt, split a batch, or complete a job's first operation. The printing system consists of a print-job queue, **printer routes** that map location and context to a device, and renderers for thermal-label ZPL and document PDF.

This system is separate from generated PDFs such as invoices, purchase orders, quotes, and job travelers. Those documents are delivered as files; printing sends output to a **physical printer**. Its default output is a thermal ZPL label.

## Two renderers, one queue

A print job renders in one of two formats, chosen by the printer it's routed to:

* **ZPL** is the text command language thermal label printers speak. It's the default for part and storage labels. A ZPL label needs a media size with ZPL dimensions configured, or it can't render.
* **PDF** is a real document, rendered by the same React-PDF engine that produces invoices and travelers. Kanban cards are **PDF only**, so a kanban card sent to a ZPL-only printer fails unless a template covers it.

Three document types exist (`documentTypeRegistry` in `packages/web/printing/src/registry.ts`):

FieldType

<details>
  <summary>**productLabel**`zpl default`</summary>

  Labels for tracked entities (serial or batch numbers). Printed from a `Receipt`, `Shipment`, `Operation`, `Entity`, `Job`, `Split`, or `StockTransfer`.
</details>

<details>
  <summary>**storageUnitLabel**`zpl default`</summary>

  Labels for shelves, bins, and storage locations. Printed from a `StorageUnit`.
</details>

<details>
  <summary>**kanbanCard**`pdf only`</summary>

  Replenishment cards for kanban bins. Printed from a `Kanban`.
</details>

The source document is what maps to the doc types worth printing. Posting a shipment yields product labels; scanning a storage unit yields a storage label. `getDocumentTypesForSource` does that lookup.

## Printer routes

A **printer route** is one physical printer, added on **Settings → Printing → Printers** with **Add Printer**. Each route carries:

FieldType

<details>
  <summary>**name**required`text`</summary>

  What you call the printer, unique per location. "e.g. Zebra 2x1".
</details>

<details>
  <summary>**format**required`"zpl" | "pdf"`</summary>

  "ZPL (Thermal Label)" or "PDF (Document)".
</details>

<details>
  <summary>**mediaSizeId**required`text`</summary>

  The label size this printer loads. ZPL printers only offer thermal (ZPL-capable) sizes; PDF printers offer all.
</details>

<details>
  <summary>**printerUrl**required`url`</summary>

  The HTTP endpoint the rendered label is POSTed to. It must be a valid URL.
</details>

<details>
  <summary>**apiKey**`text`</summary>

  Optional key sent as an `X-API-Key` header on delivery. It is stored encrypted.
</details>

<details>
  <summary>**templateId**`text`</summary>

  Optional template id. "Leave blank for built-in" — leave empty to use Alforse's built-in renderers.
</details>

<details>
  <summary>**locationId**`text`</summary>

  The location the printer lives at (optional; a route can be company-wide).
</details>

You manage routes under **Settings → Printing**. Each route has a **Test** action that sends a small ZPL test label straight to its `printerUrl`, so you can prove a printer is reachable before wiring it into a workflow. Test print only works for ZPL printers with a media size, and returns `"Test label sent to printer"` on success.

NOTE

Alforse never talks to a printer's raw port. Delivery is an HTTP POST of the rendered bytes to `printerUrl`, so a printer on your shop-floor network needs an HTTP endpoint that Alforse can reach (a print gateway or a network printer's HTTP interface). The destination must be an approved printer origin for the company; Alforse refuses to send to one that is not.

## Assignments: which printer, in which context

A route is the printer; an **assignment** decides when it's used. Assignments are set on **Settings → Printing → Assignments**, one block per location. Within a location you assign a printer to each **context** (`printerContexts` in `packages/web/printing/src/assignments.ts`):

FieldType

<details>
  <summary>**default**</summary>

  The location's fallback printer. Every other context inherits this one when it has no printer of its own.
</details>

<details>
  <summary>**shipping**</summary>

  Used when the source document is a `Shipment`.
</details>

<details>
  <summary>**receiving**</summary>

  Used when the source document is a `Receipt`.
</details>

<details>
  <summary>**inventory**</summary>

  Used for a `StockTransfer` or a `StorageUnit`.
</details>

<details>
  <summary>**workCenter**</summary>

  Per work center, for labels printed from a job operation. A work center with no assignment inherits the location default outright.
</details>

Each context row also carries an **Auto-print** switch. When it's on, the matching business event prints without a click. `getPrinterContextForSource` maps the source document to its context, and `resolveContextAssignment` walks the fallback: an unassigned context uses the location's default printer.

HEADS UP

The auto-print switch defaults **on** for a context that has no explicit entry, even when the location default has auto-print off. If you want a location's shipping station silent, set its shipping row explicitly, don't rely on the default row.

## The print-job queue

Every print, manual or automatic, becomes a **print job** and moves through a fixed set of statuses:

<details>
  <summary>**generating**</summary>

  The job exists; its content is being rendered (ZPL text or a base64 PDF).
</details>

<details>
  <summary>**queued**</summary>

  Rendered and ready. Alforse queues delivery to the printer.
</details>

<details>
  <summary>**printing**</summary>

  Handed to the printer endpoint; delivery is in flight.
</details>

<details open>
  <summary>**completed**</summary>

  Delivered, or rendered with no printer to send to (a download-only job).
</details>

<details>
  <summary>**failed**</summary>

  Rendering or delivery failed, or delivery could not be confirmed. The `error` field holds the reason and `attempts` counts tries.
</details>

Each job records its `origin`, one of **auto** (an automatic print behind a business event), **manual** (someone clicked print), or **reprint** (re-run from the jobs list). It also keeps the `sourceDocument`, `sourceDocumentId`, a readable id, the `contentType`, and the `printerUrl` it was sent to.

You watch the queue under **Settings → Printing → Print jobs**. The table streams in real time and shows status, description, source, type, origin, and when. Expanding a row exposes the printer URL, attempts, timestamps, and any error. From there you can **View** the content (ZPL previews via Labelary, PDF via an inline frame), **Reprint** it (a fresh job with `origin: "reprint"`), or **Delete** it.

NOTE

A delivery whose result can't be confirmed (a timeout, or the result couldn't be recorded) is marked failed and is **never resent automatically**, because the label may already have printed. Check the printer, then **Reprint** if needed.

## Auto-print behavior

After a supported business event, Alforse resolves the printer configuration for the location and context, checks `autoPrint` (defaults to **on** when unset), and enqueues a job. Printing failures do not block the receipt, shipment, or job that triggered them.

The wired-in events:

| Event | Context | Prints |
| - | - | - |
| Post a receipt | receiving | Product labels for received tracked entities. |
| Post a shipment | shipping | Product labels, plus `Split` labels for batch splits. |
| Split a stock-transfer line | inventory | A `Split` label, and an `Entity` reprint of the original batch. |
| Complete the first operation on a job (MES) | work center | The job's product label. |

To stop a station auto-printing, turn off Auto-print on that location's context row. To trigger a print by hand instead, use the **Print** button on the relevant screen, or send a source document to the manual print route (`/x/print`), which validates the request and enqueues the same kind of job.

HEADS UP

Automatic prints for the same source document are **deduplicated within 30 seconds**. Alforse drops the second event with a non-retriable error. Manual prints and reprints are not deduplicated.

## Print vs download

When a location has printer routes, **Print** opens a printer picker with the resolved default selected and enqueues a job after confirmation. With no routes, it opens a **download** modal for saving the label as ZPL or PDF.

## Label sizes

Media sizes are a fixed catalog (`packages/web/utils/src/labels.ts`): `label2x1`, `label4x2`, metric `label100x50mm` and `label50x25mm`, and PDF-only `avery5163`. Only sizes with ZPL dimensions can print to a thermal printer; a PDF-only size like Avery can't.

Two places set a size. A printer route pins its own `mediaSizeId` (the size loaded in that physical printer). And the company has a **default product-label size** (`companySettings.productLabelSize`), set on the document-template screen, used by the interactive label-download routes when no route dictates one. See [Company settings](/docs/reference/company-settings.md) for where that default lives, and [Documents](/docs/reference/documents.md) for the customizer that styles the built-in label templates.

**Company settings**

Where the default product-label size and other company-wide printing defaults live.

[Open](/docs/reference/company-settings.md)

**Inventory & locations**

Storage-unit labels and the tracked entities most print jobs describe.

[Open](/docs/reference/inventory.md)

**Traceability**

The serial and batch numbers a product label carries.

[Open](/docs/reference/traceability.md)

**Kanban**

Replenishment bins and the PDF cards you print for them.

[Open](/docs/reference/kanban.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.