zplCloud CLI

Self-contained single binary for macOS, Linux, Raspberry Pi and Windows. Authenticates against api.zplcloud.com with an API key. Applies printer profiles, sends raw ZPL and print jobs in other printer languages (TCP 9100 / USB), provisions Weblink certificates, runs the outbound proxy agent and watch-folder printing.

Functions

  • profiles apply - executes a saved printer profile (SGD/ZPL command sequence with variables) on a printer over TCP 9100 or USB.
  • send - sends raw ZPL from an argument or file, and print jobs in other printer languages (TSPL, EPL2, DPL, SBPL, CPCL, ESC/POS, Brother raster, PCL, Easy Plug, TPCL, JScript) byte-exact - finished, or converted from ZPL with --language.
  • weblink setup - generates the Weblink certificates server-side, uploads them, sets weblink.ip.conn2.location, resets the printer.
  • proxy - remote agent: one outbound SignalR connection to api.zplcloud.com; jobs from the "Remote Printers" tab are forwarded to LAN printers (TCP 9100) or USB printers. No inbound ports.
  • font - manage fonts and files in printer memory: list, upload (~DY), delete and install the platform font ZPLCLOUD.TTF - straight at the printer or through a remote printer.
  • remote-printers list - list the account's remote printers with ID, target and agent status.
  • USB - on Windows two built-in channels (WinUSB and the USBPRINT interface of usbprint.sys, both bidirectional and without the spooler); on Linux/macOS the libusb printer class or a virtual COM port.
  • Watch folders - the agent prints every new .zpl/.prn file of configured folders, finished jobs such as .tspl byte-exact, and converts ZPL for other printers on request (see below).

Installation

The installer detects OS and architecture and downloads the binary from https://zplcloud.com/downloads/.

macOS / Linux / Raspberry Pi

curl -fsSL https://zplcloud.com/install-cli.sh | sh
# or: wget -qO- https://zplcloud.com/install-cli.sh | sh
zplcloud --version

Targets: osx-x64, osx-arm64, linux-x64, linux-arm64 (64-bit Pi OS), linux-arm (32-bit Raspberry Pi).

Windows (PowerShell, no admin)

irm https://zplcloud.com/install-cli-pshell | iex
zplcloud --version

Installs to %LOCALAPPDATA%\zplcloud\zplcloud.exe and adds the directory to the user PATH (new terminals). Targets: win-x64, win-arm64.

Docker

Agent as a container for Raspberry Pi, Synology and any Docker host: CLI as Docker container.

API key

Created in the platform under API. Passed via environment variable or --api-key:

export ZPLCLOUD_API_KEY=sk_zplcloud_xxxx          # bash/zsh
$env:ZPLCLOUD_API_KEY='sk_zplcloud_xxxx'        # PowerShell
zplcloud profiles list --api-key sk_zplcloud_xxxx   # per command

Commands

zplcloud profiles list

Lists the printer profiles of the account (ID, name, target configuration).

zplcloud profiles list

zplcloud profiles apply

Executes all commands of a profile in sequence on one printer; each command's response is printed live.

zplcloud profiles apply 42 --target 10.0.0.5
zplcloud profiles apply "Factory Reset" --target printer01.local --vars-file vars.json
ParameterMeaning
<id-or-name>Profile ID (number) or profile name (quoted).
--target <ip|hostname>TCP target of the printer; default port 9100.
--port <n>TCP port (default 9100).
--usbSend over a USB virtual COM port instead of TCP.
--serial-port <name>Explicit serial port (COM3, /dev/cu.usbmodem1101, …); empty = auto-detect Zebra printer.
--var name=valueSet a command variable (repeatable).
--vars-file file.jsonJSON object with variable values.
--non-interactiveFail instead of prompting for missing variables.
--timeout <ms>Connect/read timeout (default 5000).

zplcloud cookbook list

Lists the ZPL cookbook recipes (command snippets with variables).

zplcloud cookbook list

zplcloud send

Sends raw ZPL. A missing line terminator is appended (\r\n), because the Zebra SGD parser does not process a command without CRLF. Print jobs in other printer languages are the exception: they go out byte-exact, without a line terminator (see Other printer languages).

The printer's answer is read automatically whenever the command produces one: SGD queries (! U1 …), JSON config ({}{"allconfig":null}), host status (~HS, ~HI, ~HQ…) and ZPL host commands (^HW directory, ^HH configuration). --read forces it. If a sleeping printer stays silent, it is woken with ~HS and the query is repeated once - queries only, a setvar is never sent twice. The proxy agent follows the same rule.

zplcloud send --target 10.0.0.5 --zpl "^XA^FO50,50^ADN,36,20^FDHello^FS^XZ"
zplcloud send --target 10.0.0.5 --file label.zpl
ParameterMeaning
--target <ip|hostname>TCP target (required unless --usb).
--port <n>TCP port (default 9100).
--zpl "<zpl>"ZPL inline (either --zpl or --file). Use --file for SGD commands with quotes to avoid shell escaping.
--file <path>Send a ZPL or SGD file (read as UTF-8 text, CRLF appended when missing). Files with a printer-language extension (.tspl, .bin …) go byte-exact. Firmware: zplcloud firmware.
--rawSend --file byte-exact whatever its extension: no text decoding, no line terminator, no answer.
--language <id>Convert the ZPL to another printer language through the API, then send the job byte-exact (API key required, see below).
--remote-printer <id|name>Send through the cloud to the agent in the printer's LAN instead of directly: ZPL via /v1/remote-printers/{id}/send, print jobs via /v1/remote-printers/{id}/raw. API key required.
--usb / --serial-portSend over USB instead of TCP (see profiles apply).

Other printer languages (TSPL, DPL, Brother …)

The ZPL → printer language tools and POST /v1/tools/zpl-to-… turn a ZPL label into a finished job for TSC, Honeywell/Datamax, SATO, Brother, Toshiba, cab, Avery Dennison/Novexx, Epson or HP printers. Such a job contains binary image data, so it must reach the printer unchanged. The CLI does that in three ways:

WayCommandNeeds
Finished job, directlyzplcloud send --target 10.0.0.7 --file label.tspl
zplcloud send --usb --file label.bin
nothing - no API key
Convert ZPL, then sendzplcloud send --target 10.0.0.7 --file label.zpl --language tspl --dpi 300 --compressAPI key (counts renders like the tool)
Through a remote printerzplcloud send --remote-printer 4 --file label.dpl
zplcloud send --remote-printer Warehouse --file label.zpl --language brother --model td-4-300
API key; agent v1.2.22+ for TCP printers, v1.2.26+ for USB printers

Files with the extensions .tspl .epl .dpl .sbpl .cpcl .escpos .bin .pcl .ezp .tpcl .jscript - the names the tools give their downloads - are sent byte-exact automatically; --raw does the same for any other file. Up to v1.2.25 the CLI read every file as text and appended CRLF, which breaks binary jobs - update the CLI and the agent to v1.2.26.

Option (with --language)Meaning
--language <id>tspl, epl2, dpl, sbpl, cpcl, escpos, brother, pcl, easyplug, tpcl or jscript.
--dpi <n>Resolution of the target printer (allowed values per language as in the tool). Brother: comes from --model.
--width-mm / --height-mmLabel size; default from ^PW/^LL, otherwise 75 × 50 mm.
--compressCompressed image format where the language has one (PCX, TIFF PackBits, TOPIX, #YIR).
--hexSBPL and CPCL: image data as hex text.
--cut falseESC/POS and Brother: do not cut after the job.
--paper a4|letterPCL: paper size.
--model <id> / --media continuous|diecutBrother: model family (e.g. ql-820, td-4-300, pt-p900) and media type.
--save <path>Also keep the converted job as a file.

zplcloud font

Manages fonts and files in printer memory - the same mechanics as the "Fonts/Files" dialog in the platform: list via ! U1 getvar "file.dir", delete via ^ID, upload as ~DY{drive}:{name},B,T,{size},, followed by the raw bytes. After the upload file.dir verifies that the file really is in flash (the printer needs a few seconds for that).

# straight from the local LAN (no API key)
zplcloud font list   --target 10.0.0.5
zplcloud font upload --target 10.0.0.5 --file myfont.ttf --name MYFONT.TTF --replace
zplcloud font delete --target 10.0.0.5 --name E:MYFONT.TTF

# install the platform font (bytes come from the cloud, API key required)
zplcloud font install --target 10.0.0.5 --api-key sk_zplcloud_xxxx
zplcloud font install --target 10.0.0.5 --file /path/ZPLCLOUD.TTF   # without the cloud

# through a remote printer: the agent in that printer's LAN does the transfer
zplcloud remote-printers list
zplcloud font install --remote-printer 4
ParameterMeaning
list | install | upload | deleteList, install the platform font, upload any TTF, delete a file.
--target <ip> / --usbDirect printer access (TCP 9100 or USB).
--remote-printer <id|name>Through the cloud to the agent in the printer's LAN (/v1/remote-printers/{id}/file). Needs an API key and an agent from v1.2.22.
--drive <E|R>Target drive: E: flash (default, survives a restart), R: RAM.
--file <ttf>Local file. Optional for install - otherwise the font comes from the cloud.
--name <NAME>Name in printer memory (default: the file name, or ZPLCLOUD.TTF).
--replaceDelete an existing file of that name first (install always does).

ZPLCLOUD.TTF is about 10 MB - E: needs the space; the existing file is deleted first. Upload through a remote printer runs over TCP 9100 only, not USB.

zplcloud remote-printers list

Lists the account's remote printers with ID, target, assigned agent and its status. The IDs are what font --remote-printer and send --remote-printer expect.

zplcloud remote-printers list --api-key sk_zplcloud_xxxx

zplcloud weblink setup

Generates the three NRD certificate files server-side (API key required), uploads them via ~DYE, sets weblink.ip.conn2.location and resets the printer. Reads the printer serial and registers owner + API key in the backend (also for replacement devices).

zplcloud weblink setup --printer printer01 --endpoint https://weblink.zplcloud.com --target 10.0.0.5 --api-key sk_zplcloud_XXXX

# Test stage
zplcloud weblink setup --printer printer01 --endpoint https://weblink.test.zplcloud.com --target 10.0.0.5 \
  --api-base https://api.test.zplcloud.com --api-key <test-stage-key>
ParameterMeaning
--printer <hostname>Printer name (certificate scope / NRD storage).
--endpoint <url>Weblink URL: https://weblink.zplcloud.com (prod) or https://weblink.test.zplcloud.com (test).
--target / --portTCP target + port (default 9100).
--api-key / --api-baseAPI key (required). Test stage: key + api-base of the test backend.

Without CLI: Weblink certificate deployment (server-generated PowerShell/bash one-liner).

zplcloud proxy

Starts the remote agent. Outbound SignalR connection to api.zplcloud.com; jobs from the "Remote Printers" tab are forwarded to printers in the local LAN (TCP 9100) or USB.

zplcloud proxy --agent "Warehouse Berlin"
zplcloud proxy --agent "PI" --verbose --log --log-dir ./logs/zplcloud-logs
ParameterMeaning
--agent <name>Display name in the Remote Printers tab (required).
--timeout <ms>Connect/read timeout (default 5000).
--verbose | --debugSignalR negotiation/transport/handshake trace with timestamps.
--logWrite all console output to zplcloud-<yyyy-MM-dd>.log.
--log-dir <dir>Log directory (default: current directory).
--service-installInstall as system service (systemd / launchd / scheduled task). Needs --agent + API key.
--service-uninstallRemove the service (no API key needed).

zplcloud proxy --service-install

Installs the agent as a service that starts at boot/login and reconnects automatically. API key, agent name, API base and timeout are written into the service definition.

zplcloud proxy --agent "Warehouse Berlin" --service-install --api-key sk_zplcloud_xxxx
SystemMechanismStart
Linux / Raspberry Pisystemd unit zplcloud-agent.service (Restart=always, RestartSec=5)at boot (sudo required, detected automatically)
macOSlaunchd LaunchAgent ~/Library/LaunchAgents/com.zplcloud.agent.plist (KeepAlive=true)at login, no admin
Windowsscheduled task zplcloud-agent (schtasks ONLOGON, /RL LIMITED)at login, no admin
# Status
Linux/Pi : systemctl status zplcloud-agent   |  journalctl -u zplcloud-agent -f
macOS    : launchctl list | grep zplcloud
Windows  : schtasks /Query /TN zplcloud-agent

# Uninstall
zplcloud proxy --service-uninstall

Optional: --timeout <ms>, --log-dir <dir> (defaults: Linux /var/log/zplcloud, macOS /tmp, Windows %LOCALAPPDATA%\zplcloud\logs). In Docker, restart: unless-stopped replaces the service (see CLI as Docker container).

zplcloud usb list

Lists serial ports and the Zebra USB channels. On Windows there are two built-in paths, both bidirectional and without the spooler or a third-party driver:

ChannelWhen it applies
WinUSBThe printer offers WinUSB natively (ZD421: usb.device.mode = composite) and Windows has registered the interface.
USBPRINTFallback through the device interface of usbprint.sys, opened directly with CreateFile. Needed when Windows registers no WinUSB interface for the printer - which happens even though winusb.sys is bound.

The CLI tries WinUSB first, then USBPRINT, then the virtual COM port. On Linux/macOS it uses the libusb printer class.

zplcloud usb list

zplcloud version / help

zplcloud version / --version prints the version, zplcloud help / --help the full help.

Data sources on the agent

Besides printers, the agent can also connect databases. The query then runs inside your network, the connection string stays local and only the result rows travel over the wire. Each engine has its own namespace; all three ways (option, environment variable, file) are equivalent. Details, query rules and the agentless cloud route are documented under Connectors.

EngineOptionEnvironment variableFile
SQL Server--sql NAME=…ZPLCLOUD_SQL_<NAME>_CONNECTIONsqlservers.json
MongoDB--mongo NAME=…ZPLCLOUD_MONGO_<NAME>_CONNECTIONmongoservers.json
PostgreSQL--postgres NAME=…ZPLCLOUD_PG_<NAME>_CONNECTIONpgservers.json
MySQL--mysql NAME=…ZPLCLOUD_MYSQL_<NAME>_CONNECTIONmysqlservers.json
MariaDB--mariadb NAME=…ZPLCLOUD_MARIADB_<NAME>_CONNECTIONmariadbservers.json
Kafka--kafka NAME=…ZPLCLOUD_KAFKA_<NAME>_CONNECTIONkafkabrokers.json
zplcloud proxy --agent "Warehouse" \
  --postgres WMS="Host=pg.internal.lan;Database=wms;Username=zplcloud;Password=..." \
  --mysql SHOP="Server=mysql.internal.lan;Database=shop;User ID=zplcloud;Password=..." \
  --mariadb ERP="Server=maria.internal.lan;Database=erp;User ID=zplcloud;Password=..."

Global options

OptionMeaning
--api-key <key>API key.
--api-base <url>Backend base URL (default https://api.zplcloud.com).
--log / --log-dirLogging (see proxy).

Environment variables

VariableMeaning
ZPLCLOUD_API_KEYAPI key (alternative to --api-key).
ZPLCLOUD_API_BASEBase URL (alternative to --api-base).
ZPLCLOUD_AGENTAgent name (same as --agent; used in Docker).
ZPLCLOUD_TIMEOUTConnect/read timeout in ms.

--help output

zplcloud v1.2.15
zplCloud.com CLI - provision Zebra printers from zplCloud.com printer profiles.

USAGE:
  zplcloud profiles list
  zplcloud profiles apply <id-or-name> --target <ip|hostname> [--port 9100] [--var name=value ...] [--vars-file file.json]
  zplcloud cookbook list
  zplcloud send --target <ip|hostname> [--port 9100] (--zpl "<zpl>" | --file <path>)
  zplcloud send --target <ip|hostname> --file label.tspl [--raw]
  zplcloud send --target <ip|hostname> --file label.zpl --language tspl [--dpi 203] [--compress]
  zplcloud send --remote-printer <id|name> (--zpl "<zpl>" | --file <path>) [--language <id>]
  zplcloud weblink setup --printer <hostname> --endpoint <url> --target <ip|hostname> [--port 9100]
  zplcloud proxy --agent <name> [--timeout <ms>] [--verbose|--debug] [--log [--log-dir <dir>]]
  zplcloud usb list
  zplcloud version
  zplcloud help

TRANSPORT (apply/send/weblink):
  --target <ip|hostname>   TCP target (default port 9100)
  --port <n>               TCP port (default: 9100)
  --usb                    Use a USB virtual COM port instead of TCP
  --serial-port <name>     Explicit serial port (COM3, /dev/cu.usbmodem1101, ...)
  --bluetooth              (not supported yet - reserved for future RFCOMM transport)
  --timeout <ms>           Connect/read timeout (default: 5000)

PROXY (indirect realtime sending, no inbound firewall ports):
  zplcloud proxy --agent "Warehouse Berlin" [--verbose|--debug]
  Connects OUTBOUND to api.zplcloud.com and forwards print commands from the
  'Remote Printers' tab to Zebra printers in the local LAN over TCP 9100.
  --verbose/--debug prints SignalR negotiation/transport/handshake trace
  (with timestamps) to diagnose connection problems.

VARIABLES (profiles apply):
  --var name=value         Set a command variable (repeatable)
  --vars-file file.json    JSON object with variable values
  --non-interactive        Fail instead of prompting for missing required variables

API:
  --api-key <key>          API key from the zplCloud API tab
  --api-base <url>         api.zplcloud.com base URL (default: https://api.zplcloud.com)
  Environment: ZPLCLOUD_API_KEY, ZPLCLOUD_API_BASE

LOGGING:
  --log                    Write all console output to zplcloud-<yyyy-MM-dd>.log
  --log-dir <dir>          Log directory (default: current directory)

VERSION:
  --version                Print the version and exit
  zplcloud version

Watch-folder printing

The agent watches configured folders and sends every new .zpl/.prn file to a LAN printer (TCP 9100) or USB. Rules: oldest file first, one file at a time per folder; a file is printed only when its last write is older than stabilitySeconds; *.part/*.tmp are skipped; on success the file is deleted (deleteAfterOk) or kept; failures are moved to failedSubDir.

Configured per agent in the online agent config (GET/PUT /api/remote-printers/agents/{agent}/config); changes are pushed live (UpdateAgentConfig), no restart.

{
  "modules": {
    "watchFolders": [
      {
        "name": "rawprint",
        "enabled": true,
        "folder": "/watch/rawprint",
        "pattern": "*.zpl;*.prn",
        "targetHost": "10.0.0.5",
        "targetPort": 9100,
        "targetUsb": false,
        "pollMs": 2000,
        "stabilitySeconds": 3,
        "deleteAfterOk": true,
        "failedSubDir": "failed"
      }
    ]
  }
}

Folder targets appear in printer lists as wf:<agent>:<folder> ("Agent / 📁 folder"): the platform writes the ZPL as a file into the folder, the agent prints it. Endpoints: GET /api/remote-printers/agents/{agent}/folders, POST …/folders/send; public API GET /v1/agents/folders, POST /v1/print/folder with { "agent", "folder", "zpl" }.

Watch folders for other printer languages

From agent v1.2.26 a watch folder also serves printers that do not speak ZPL, in two ways:

  • Finished jobs - files with the extensions .tspl .epl .dpl .sbpl .cpcl .escpos .bin .pcl .ezp .tpcl .jscript are sent byte-exact. Add the extension to pattern, e.g. "*.zpl;*.tspl".
  • Convert ZPL - with convert the agent turns every ZPL file into the printer's language before printing (POST /v1/tools/zpl-to-… with the agent's API key, counts renders like the tool). Your ERP or carrier software keeps writing ZPL; a TSC, Honeywell or Brother printer gets its own language. Options as in the tool: language, dpi, widthMm, heightMm, compress, hex, cut, paper, model, media.
{
  "modules": {
    "watchFolders": [
      {
        "name": "tsc-shipping",
        "enabled": true,
        "folder": "/watch/tsc",
        "pattern": "*.zpl;*.tspl",
        "targetHost": "10.0.0.7",
        "targetPort": 9100,
        "convert": { "language": "tspl", "dpi": 300, "compress": true }
      }
    ]
  }
}

The public API drops print jobs into such a folder as well: POST /v1/print/folder with { "agent", "folder", "zpl", "language": "tspl", "dpi": 300 } (the cloud converts) or { "agent", "folder", "base64", "fileName": "label.tspl" } (finished job). The file keeps the job's extension, so the folder's pattern must include it - otherwise the agent answers with an error instead of storing a file that would never print.

Examples

zplcloud profiles list
zplcloud profiles apply 42 --target 10.0.0.5
zplcloud profiles apply "GTIN-Label" --target 10.0.0.5 --var gtin=4006381333930 --var charge=LOT-42
zplcloud profiles apply 42 --target 10.0.0.5 --vars-file vars.json
zplcloud send --target 10.0.0.5 --zpl "^XA^FO50,50^A0N,50^FDHello^FS^XZ"
zplcloud send --target 10.0.0.7 --file label.tspl
zplcloud send --target 10.0.0.7 --file label.zpl --language tspl --dpi 300 --compress
zplcloud send --remote-printer 4 --file label.zpl --language dpl
zplcloud proxy --agent "Warehouse Berlin"
zplcloud usb list
zplcloud profiles apply 42 --usb --serial-port /dev/ttyACM0