CLI Tool
The zplflow command-line tool lets you manage conversions, async jobs, pipelines, and your account directly from the terminal — no code required.
Availability: Pre-built binaries for Windows, Linux, and macOS on amd64 and arm64 architectures. Published as a single static binary with zero system dependencies.
Installation
Download Pre-built Binary
| Platform | Architecture | Binary |
|---|---|---|
| Linux | amd64 | zplflow_linux_amd64 |
| Linux | arm64 | zplflow_linux_arm64 |
| macOS | amd64 (Intel) | zplflow_darwin_amd64 |
| macOS | arm64 (Apple Silicon) | zplflow_darwin_arm64 |
| Windows | amd64 | zplflow_windows_amd64.exe |
| Windows | arm64 | zplflow_windows_arm64.exe |
Download the appropriate binary from GitHub Releases and place it in your PATH.
Linux / macOS Setup
# Download (replace {version}, {os}, {arch} with your values)
curl -L -o zplflow "https://github.com/zplflow/zplflow/releases/download/v{version}/zplflow_{os}_{arch}"
# Make executable and move to PATH
chmod +x zplflow
sudo mv zplflow /usr/local/bin/
# Verify
zplflow version
Windows Setup
# Download the .exe to a directory in your PATH, e.g. C:\Tools\
# Or install via winget (coming soon):
# Verify
zplflow.exe version
Configuration
The CLI needs your API key to authenticate. Provide it via environment variable or flag:
Environment Variable (Recommended)
export ZPLFLOW_API_KEY="lb_YOUR_KEY"
This keeps the key out of your shell history.
Command-Line Flag
zplflow --api-key lb_YOUR_KEY account balance
Useful for one-off commands or scripting with a different key.
API URL
By default the CLI connects to https://api.zplflow.io. Override with ZPLFLOW_API_URL or --api-url:
export ZPLFLOW_API_URL="https://staging-api.zplflow.io"
Verbose mode
Pass --verbose to print diagnostic HTTP logs to stderr (method, full URL, status code, response time). Useful to self-debug before opening a support ticket.
zplflow --verbose convert zpl-to-pdf --file label.zpl --save out.pdf
Logs include request and response lines, but never the API key, request/response body, or presigned URL path.
Output Format
All commands support three output modes via --output:
| Value | Behavior |
|---|---|
text |
Human-readable key-value output (default) |
json |
Raw API response — ideal for piping to jq |
table |
Aligned columns for list endpoints |
Commands Reference
Contents: Account · Convert · Estimate · Jobs · Pipeline · Scopes · Version · Shell Completion
Account
zplflow account balance
Get your tenant’s token balance and reservations.
zplflow account balance
Tenant: your-company
Balance: 10,000 tokens
Reserved: 3 tokens
# JSON for scripting
zplflow account balance --output json
{"tenant_id":"your-company","balance":10000,"reserved":3}
Estimate
zplflow estimate
Estimate the token cost of a conversion before running it. No payload analysis — purely parameter-based.
# PDF to ZPL estimate
zplflow estimate --operation pdf_to_zpl --dpi 203 --documents 10
# ZPL to PDF estimate
zplflow estimate --operation zpl_to_pdf --dpmm 8dpmm --documents 5
| Flag | Type | Description |
|---|---|---|
--operation |
string | Required. pdf_to_zpl, zpl_to_pdf, or pipeline_run |
--dpi |
int | DPI for pdf_to_zpl: 203 or 300 |
--max-kb |
int | Max KB for pdf_to_zpl |
--fit |
string | Fit mode: contain, cover, or stretch |
--dpmm |
string | Density for zpl_to_pdf: 8dpmm, 12dpmm, 24dpmm |
--documents |
int | Number of documents (default: 1) |
Convert
Synchronous conversions with inline payloads. No S3, no async — ideal for real-time label printing.
zplflow convert pdf-to-zpl
Convert a PDF file to ZPL text.
# From file, output to stdout
zplflow convert pdf-to-zpl --file shipping.pdf --dpi 203
# Save to file
zplflow convert pdf-to-zpl --file labels.pdf --dpi 300 --save output.zpl
# Read from stdin (pipe)
cat shipping.pdf | zplflow convert pdf-to-zpl --stdin > label.zpl
# With dimensions and fit
zplflow convert pdf-to-zpl --file large.pdf --dpi 203 --width 101.6 --height 152.4 --unit mm --fit contain
| Flag | Type | Default | Description |
|---|---|---|---|
--file |
string | — | Input PDF file path |
--stdin |
bool | false | Read PDF from stdin |
--save |
string | — | Save ZPL to file (default: stdout) |
--dpi |
int | 203 | 203 or 300 |
--max-kb |
int | — | Max output KB per page |
--width |
float | — | Label width |
--height |
float | — | Label height |
--unit |
string | mm | mm or in |
--fit |
string | contain | contain, cover, or stretch |
--allow-degrade |
bool | false | Allow quality degradation |
--compat-mode |
bool | false | Enable compatibility mode |
--idempotency-key |
string | auto | Override auto-generated key |
zplflow convert zpl-to-pdf
Convert a ZPL string to one or more PDF pages (one per input label).
The CLI automatically decodes the JSON envelope returned by the API and writes one PDF file per page, prefixed with a 1-based index.
# Single label → 1_<save>
zplflow convert zpl-to-pdf --file label.zpl --save output.pdf
# → output.pdf becomes 1_output.pdf
# Multi-label ZPL → 1_<save>, 2_<save>, 3_<save>, ...
echo -e '^XA^FO50,50^FD1^FS^XZ\n^XA^FO50,50^FD2^FS^XZ\n^XA^FO50,50^FD3^FS^XZ' \
| zplflow convert zpl-to-pdf --stdin --save label.pdf
# → 1_label.pdf, 2_label.pdf, 3_label.pdf
# Custom density and dimensions
zplflow convert zpl-to-pdf --file label.zpl --save out.pdf --dpmm 12dpmm --width 101.6 --height 152.4
The numeric prefix is always applied — even with a single page — so the file count always equals the number of output pages. If --save includes a directory (--save /tmp/out/label.pdf), only the basename gets the prefix.
| Flag | Type | Default | Description |
|---|---|---|---|
--file |
string | — | Input ZPL file path |
--stdin |
bool | false | Read ZPL from stdin |
--save |
string | — | Required. Output PDF file path |
--dpmm |
string | 8dpmm | 8dpmm, 12dpmm, 24dpmm |
--width |
float | — | Label width |
--height |
float | — | Label height |
--unit |
string | mm | mm or in |
--index |
int | 0 | Label index within ZPL |
--idempotency-key |
string | auto | Override auto-generated key |
Note: PDF output is binary —
--saveis always required. The file path is never written to stdout.
Pipeline
zplflow pipeline apply <pipeline-id>
Apply a pre-defined pipeline transformation to ZPL documents.
# Single ZPL file
zplflow pipeline apply 550e8400-e29b-41d4-a716-446655440000 --zpl-file label.zpl
# Multiple documents via JSON file
zplflow pipeline apply 550e8400-... --json-file batch.json
# Save output to file
zplflow pipeline apply 550e8400-... --zpl-file label.zpl --save transformed.zpl
| Flag | Type | Description |
|---|---|---|
--zpl-file |
string | Single ZPL file to transform |
--json-file |
string | JSON file with array of {"zpl_base64":"..."} documents |
--save |
string | Save output to file (default: stdout) |
--idempotency-key |
string | Override auto-generated key |
The pipeline ID is the first positional argument. Find your pipelines in the Admin UI.
Jobs
Async job management for bulk conversions and large files. The CLI handles the full lifecycle: create → upload → start → poll → download.
zplflow job create
Create and automatically start an async job.
# PDF to ZPL async
zplflow job create --operation pdf_to_zpl --file labels.pdf --dpi 203
# ZPL to PDF async
zplflow job create --operation zpl_to_pdf --file label.zpl
# Pipeline run
zplflow job create --operation pipeline_run --file labels.zpl --pipeline-id 550e8400-...
Job ID: 770e8400-e29b-41d4-a716-446655440000
Status: queued
Tokens Reserved: 2
Monitor: zplflow job wait 770e8400-...
The create command uploads the input file via the presigned S3 URL and starts the job in a single step.
| Flag | Type | Description |
|---|---|---|
--operation |
string | Required. pdf_to_zpl, zpl_to_pdf, or pipeline_run |
--file |
string | Input file path |
--stdin |
bool | Read input from stdin |
--dpi |
int | DPI for pdf_to_zpl |
--max-kb |
int | Max KB for pdf_to_zpl |
--fit |
string | Fit mode: contain, cover, or stretch |
--dpmm |
string | Density for zpl_to_pdf |
--width |
float | Label width |
--height |
float | Label height |
--unit |
string | Unit: mm or in |
--pipeline-id |
string | Required for pipeline_run |
--allow-degrade |
bool | Allow quality degradation |
--compat-mode |
bool | Enable compatibility mode |
--idempotency-key |
string | Override auto-generated key |
zplflow job list
List all async jobs for your tenant.
# Last 10 jobs
zplflow job list
# Filter by status, with table output
zplflow job list --status succeeded --limit 20
# Paginated
zplflow job list --status failed --cursor next-cursor-token
JOB ID STATUS OPERATION
550e8400-e29b-41d4-a716-446655440000 succeeded pdf_to_zpl
660e8400-e29b-41d4-a716-446655440000 failed zpl_to_pdf
| Flag | Type | Default | Description |
|---|---|---|---|
--status |
string | — | Filter: created, queued, running, succeeded, failed, canceled |
--limit |
int | 10 | Max jobs to return |
--cursor |
string | — | Pagination cursor |
zplflow job get <job-id>
Get detailed status and results for a specific job.
zplflow job get 550e8400-e29b-41d4-a716-446655440000
Job ID: 550e8400-e29b-41d4-a716-446655440000
Operation: pdf_to_zpl
Status: succeeded
Tokens: 2 reserved / 2 committed
Results: 1 file(s)
[0] 12,451 bytes application/pdf
URL: https://s3.amazonaws.com/... (expires 2026-01-15T11:00:00Z)
Meta:
DPI: 203
Budget KB: 32
Degraded: false
Tile count: 1
zplflow job start <job-id>
Start a job that is in created state.
zplflow job start 550e8400-...
| Flag | Type | Description |
|---|---|---|
--idempotency-key |
string | Override auto-generated key |
zplflow job cancel <job-id>
Cancel a queued or running job. Reserved tokens are rolled back.
zplflow job cancel 550e8400-...
| Flag | Type | Description |
|---|---|---|
--idempotency-key |
string | Override auto-generated key |
zplflow job wait <job-id>
Poll a job until completion and automatically download the result. The recommended way to handle async jobs.
# Wait for result and save to file
zplflow job wait 550e8400-... --save result.pdf
# Custom timeout (default 5 minutes)
zplflow job wait 550e8400-... --save result.zpl --timeout 10m
# Without --save, prints text results to stdout (use for ZPL output)
zplflow job wait 550e8400-...
Waiting for job 550e8400-... [created] 1s
Waiting for job 550e8400-... [queued] 2s
Waiting for job 550e8400-... [running] 4s
Job succeeded! Saved 12,451 bytes to result.pdf
Polling uses exponential backoff starting at 1 second and capping at 8 seconds.
| Flag | Type | Default | Description |
|---|---|---|---|
--save |
string | — | File to save result to (required for binary output) |
--timeout |
duration | 5m | Max wait time (e.g. 30s, 5m, 1h) |
--interval |
duration | 1s | Initial poll interval |
Scopes (catalog browsing)
The zplflow scopes subcommands inspect and print the scope catalog that the API uses to enforce per-key permissions. They hit the public GET /v1/scopes endpoint and therefore do not require ZPLFLOW_API_KEY — no bearer is sent and no token is consumed.
zplflow scopes list
Print every scope known to the running deployment, grouped by category.
# Human-readable grouped table (default)
zplflow scopes list
SCOPE ID GROUP PUBLIC ENDPOINTS
convert:pdf_to_zpl convert no POST /v1/convert/pdf-to-zpl
convert:zpl_to_pdf convert no POST /v1/convert/zpl-to-pdf
convert:estimate convert no POST /v1/estimate
jobs:create jobs no POST /v1/jobs
jobs:read jobs no GET /v1/jobs, GET /v1/jobs/{id}
jobs:write jobs no POST /v1/jobs/{id}/start, POST /v1/jobs/{id}/cancel
pipelines:read pipelines no GET /v1/pipelines, GET /v1/pipelines/{id}
pipelines:write pipelines no POST /v1/pipelines, DELETE /v1/pipelines/{id}
pipelines:apply pipelines no POST /v1/pipelines/preview, POST /v1/pipelines/{id}/apply
pipelines:prepackaged:read pipelines yes GET /v1/pipelines/prepackaged*, POST .../adopt
account:read account no GET /v1/account/balance
account:export account no GET /v1/account/export
audit:read audit no GET /v1/audit/events
gdpr:delete:tenant gdpr no DELETE /v1/tenants/{id}
gdpr:delete:user gdpr no DELETE /v1/users/{email}
gdpr:breach_contact gdpr no PUT /v1/tenants/{id}/breach-contact
apikeys:manage admin no GET/POST/DELETE /v1/apikeys
| Flag | Description |
|---|---|
--embedded |
Print inline as one [scope_id, scope_id, ...] array, ready to paste into a POST /v1/apikeys body |
--output table |
Aligned columns (default) |
--output json |
Raw catalog as returned by GET /v1/scopes, suitable for piping into jq |
Machine-readable JSON
zplflow scopes list --output json | jq '.scopes | length'
{
"version": "1",
"description": "zplflow API+MCP scope catalog...",
"scopes": [
{
"id": "convert:pdf_to_zpl",
"group": "convert",
"description": "Convert PDF documents to ZPL (sync, inline response).",
"public": false,
"tenant_feature_required": "",
"http_endpoints": [
{ "method": "POST", "path": "/v1/convert/pdf-to-zpl" }
],
"mcp_tools": ["convertPDFToZPL"]
}
]
}
Emit a scope list ready to copy into POST /v1/apikeys
zplflow scopes list --embedded
["convert:pdf_to_zpl","convert:zpl_to_pdf","jobs:create","jobs:read","jobs:write","pipelines:read","pipelines:apply"]
zplflow scopes show <scope_id>
Print full details for a single scope, including its MCP tools, tenant-feature prerequisite, and public flag.
zplflow scopes show pipelines:apply
ID: pipelines:apply
Group: pipelines
Public: no
Tenant feature required: pipelines
HTTP endpoints:
- POST /v1/pipelines/preview
- POST /v1/pipelines/{id}/apply
MCP tools:
- applyPipeline
Description: Preview a pipeline or apply it to 1-5 ZPL documents.
zplflow scopes show pipelines:prepackaged:read --output json
{
"id": "pipelines:prepackaged:read",
"group": "pipelines",
"description": "Browse and adopt prepackaged pipeline templates (always public).",
"public": true,
"tenant_feature_required": "",
"http_endpoints": [
{ "method": "GET", "path": "/v1/pipelines/prepackaged" },
{ "method": "GET", "path": "/v1/pipelines/prepackaged/{id}" },
{ "method": "POST", "path": "/v1/pipelines/prepackaged/{id}/adopt" }
],
"mcp_tools": []
}
Tip: The --output json form is identical to the live API response, so you can use it as a fixture in tests: zplflow scopes show apikeys:manage --output json > fixtures/scope_apikeys_manage.json.
Notes
- These commands never send
Authorization. They are safe to run on developer machines without exposing credentials. - The catalog returned by the CLI is fetched from the same
GET /v1/scopesendpoint your application code uses, so a configured proxy (ZPLFLOW_API_URL) and custom scope builds are visible immediately. - For deeper integration (issuing scoped keys, rotating scoped keys) see Self-Service API Keys.
Shell Completion
Generate tab-completion for your shell.
# Bash
zplflow completion bash > /etc/bash_completion.d/zplflow
# Zsh
zplflow completion zsh > "${fpath[1]}/_zplflow"
# Fish
zplflow completion fish > ~/.config/fish/completions/zplflow.fish
# PowerShell
zplflow completion powershell > zplflow.ps1
Version
zplflow version
zplflow version 1.2.3 (commit: abc1234, built: 2026-01-15T10:00:00Z)
go version go1.25.4 linux/amd64
# Short form — handy for scripting
zplflow version --short
1.2.3
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
API error or invalid parameters |
2 |
Usage error (missing required flag) |
3 |
Authentication error (missing/invalid API key) |
4 |
Network or timeout error |
Use exit codes in scripts:
#!/bin/bash
if zplflow job wait "$JOB_ID" --save result.zpl --timeout 5m; then
echo "Done: $(wc -c < result.zpl) bytes"
else
code=$?
echo "Failed with exit code $code"
fi
Idempotency
All POST commands automatically generate a unique Idempotency-Key (UUID v4) per request. This prevents duplicate operations on network retries.
The generated key is printed to stderr:
Idempotency Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
To reuse a key for explicit retry control:
zplflow convert pdf-to-zpl --file labels.pdf --idempotency-key my-retry-001
Typical Workflows
Quick Sync Convert
export ZPLFLOW_API_KEY="lb_YOUR_KEY"
# PDF → ZPL (result to stdout)
zplflow convert pdf-to-zpl --file shipping.pdf --dpi 203 > label.zpl
# ZPL → PDF (result to file)
zplflow convert zpl-to-pdf --file label.zpl --save output.pdf
Async Batch Processing
# Step 1: Create and start job
JOB_ID=$(zplflow job create --operation pdf_to_zpl --file labels.pdf --dpi 203 --output json | jq -r '.job_id')
echo "Job: $JOB_ID"
# Step 2: Wait for completion
zplflow job wait "$JOB_ID" --save result.zpl
Pipeline in a CI/CD Pipeline
# Apply pipeline transformation
zplflow pipeline apply "$PIPELINE_ID" --zpl-file label.zpl --save transformed.zpl
# Then convert to PDF for preview
zplflow convert zpl-to-pdf --file transformed.zpl --save preview.pdf
Error Messages
The CLI translates API errors into human-readable messages:
| HTTP | Error Code | CLI Output |
|---|---|---|
| 401 | UNAUTHORIZED |
Invalid API key. Set ZPLFLOW_API_KEY env var or use --api-key flag. |
| 402 | INSUFFICIENT_TOKENS |
Not enough tokens — ... |
| 403 | FORBIDDEN |
Tenant suspended or access denied. |
| 409 | IDEMPOTENCY_CONFLICT |
Idempotency key conflict — different payload used with the same key. |
| 429 | RATE_LIMITED |
Rate limit exceeded. Try again in a few seconds. |
Security
- The API key is never logged or printed in output
- Use the environment variable (
ZPLFLOW_API_KEY) to avoid leaking keys in shell history - Presigned S3 URLs are never logged
- Payload content (PDF/ZPL) is never logged
- Gzip compression is transparent: the CLI sends
Accept-Encoding: gzipand compresses large request bodies automatically
Next Steps
- Authentication — Full auth reference and scope model
- Conversions — Sync API parameters and pricing
- Jobs API — Async workflow details
- Pipelines — ZPL transformation recipes
- Self-Service API Keys — programmatic key management
- Best Practices — Production integration guide