Connectors

Five databases, seven message buses and two object stores - in both directions. This page lists the connection string formats, the agent's environment variables, the envelope format for ZPL held in object storage, and the output targets that store and notify after the print.

Overview

A label needs two ingredients: data and a design. The data either comes on demand from a database or arrives on its own through a message bus. Finished ZPL may also sit inside the message or live in object storage. After the print, zplCloud can store the result and say so.

Connectors: databases, message buses and object storage in; printer and archive out

Data sources (data hub)

A data source is a stored query against one of your databases, and there are two routes to it. On the agent route the zplCloudCli agent runs the query inside your network; the connection string lives only there and never leaves - only the result rows travel over the wire. On the cloud route the backend connects by itself, for example to an Azure Database for PostgreSQL or Amazon RDS; the connection string is then stored AES-encrypted in the platform database and decrypted only to run the query.

TypeEngineRoute
agent-sqlMicrosoft SQL ServerAgent
agent-mongoMongoDBAgent
agent-postgresPostgreSQLAgent
agent-mysqlMySQLAgent
agent-mariadbMariaDBAgent
cloud-postgresPostgreSQLBackend direct
cloud-mysqlMySQLBackend direct
cloud-mariadbMariaDBBackend direct

The data hub is a Pro feature. It is used by print views, DHL lookups and batch printing.

Configuring databases on the agent

The agent learns about its databases in three ways: as a command line option, as an environment variable, or from a file next to the binary or under ~/.zplcloud/. Each engine has its own namespace so the choice in the UI stays unambiguous.

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
# All in one call (every option may be repeated):
zplcloud proxy --agent "Warehouse" \
  --postgres WMS="Host=pg.internal.lan;Database=wms;Username=zplcloud;Password=...;SSL Mode=Require" \
  --mysql SHOP="Server=mysql.internal.lan;Database=shop;User ID=zplcloud;Password=..." \
  --mariadb ERP="Server=maria.internal.lan;Database=erp;User ID=zplcloud;Password=..."

# or as environment variables (Windows PowerShell: $env:ZPLCLOUD_PG_WMS_CONNECTION = "...")
export ZPLCLOUD_PG_WMS_CONNECTION="Host=pg.internal.lan;Database=wms;Username=zplcloud;Password=..."
zplcloud proxy --agent "Warehouse"

# or one file per engine, next to the binary or under ~/.zplcloud/
# pgservers.json      { "postgresServers": { "WMS":  "Host=...;Database=..." } }
# mysqlservers.json   { "mysqlServers":    { "SHOP": "Server=...;Database=..." } }
# mariadbservers.json { "mariadbServers":  { "ERP":  "Server=...;Database=..." } }

What a query may contain

The same rules apply to every relational source, agent or cloud. They are deliberately tight: a data source is there to read, not to write.

  • A single SELECT (or WITH) only - no ;, no comments, no multi-statements.
  • Filter values always reach the database as parameters (@p0), never through string concatenation.
  • Filter, sort and LIMIT/OFFSET are applied in the source (pushdown), not in the platform's memory.
  • At most 1000 rows per fetch, 15 second timeout. Larger sets are fetched server-side in chunks.

Message buses

A subscription connects a bus to a printer. Every incoming message becomes a record, fills the bindings of a design and prints - or carries finished ZPL as an envelope. Kafka runs either straight from the cloud or on-prem through the agent; every other bus runs straight from the cloud.

TypeConnection stringTopic field
cloud (Kafka)BootstrapServers=broker:9092;GroupId=zplCloudConsumertopic
agent (Kafka on-prem)lives on the agent (--kafka NAME=…)topic
servicebusEndpoint=sb://…;SharedAccessKeyName=…;SharedAccessKey=…queue or topic/Subscriptions/name; optional when the string carries EntityPath
mqttServer=broker;Port=8883;Tls=true;Username=…;Password=…;Qos=1topic filter (+ and # allowed)
amqp10amqps://user:password@broker:5671address (queue or node)
rabbitmqamqp://user:password@rabbit:5672/vhostqueue name (not created automatically)
sqsQueueUrl=…;Region=eu-central-1;AccessKey=…;SecretKey=…queue URL; optional when QueueUrl is in the string
pubsubProjectId=…;SubscriptionId=…;CredentialsBase64=…subscription id; optional when SubscriptionId is in the string

For Amazon SQS, leaving out AccessKey/SecretKey falls back to the normal AWS credential chain (IAM role, environment, profile). For Google Pub/Sub the service account JSON is passed Base64 encoded, because the JSON itself contains semicolons and line breaks that would otherwise split the key=value pairs.

Acknowledgement: nothing is lost silently

Every bus follows the same rule: nothing is acknowledged before the print. As long as the ZPL has not reached the printer, the message counts as open and is delivered again.

BusOn successOn print failure
Kafkacommit the offsetoffset stays put, the message comes back
Azure Service Buscomplete (PeekLock)abandon with backoff, dead-letter after 3 deliveries
MQTTackno ack - the broker redelivers at QoS 1/2
AMQP 1.0acceptrelease, the broker redelivers
RabbitMQBasicAckBasicNack with requeue
Amazon SQSDeleteMessagenot deleted, redelivered after the visibility timeout (then redrive/DLQ)
Google Pub/Subacknack, Pub/Sub redelivers per its retry policy

ZPL envelope and ZPL from object storage

A subscription without a design expects finished ZPL in the message. The printer field names the target serial, rp:{id} for a remote printer, or "*" for every printer on the account.

{
  "schemaVersion": 1,
  "printer": "*",
  "zpl": "^XA^FO50,50^A0N,40,40^FDHello^FS^XZ"
}

Large print files do not belong inside a bus message. Instead of embedding the ZPL, the message points at it: storage is the name of a storage connection, key the object key inside it. The file is only fetched at print time. The name resolves within your own or your company's scope only - someone else's connection cannot be reached this way.

{
  "schemaVersion": 1,
  "printer": "rp:7",
  "zplRef": { "storage": "archive", "key": "orders/4711.zpl" }
}

Storage connections (S3 and Azure Blob)

A storage connection is created once and used in both directions: for reading through zplRef and for writing through an output target. The credentials are stored AES-encrypted in the platform database; the UI only shows whether credentials exist, never their value.

ProviderBucket / containerCredentialsNotes
s3bucketAccessKey=…;SecretKey=… (optional SessionToken)Without credentials the AWS credential chain applies. Custom Endpoint plus path style for MinIO, R2, Wasabi, Backblaze and Ceph.
azureblobcontainerconnection string or container SAS URLWith a SAS URL the link already points at the container.

Output targets: store it and say so

An output target bundles up to three steps that run after a successful print. Each one is switched on separately, and all of them run after the print: a failure there never stops the print, it only lands in the subscription log.

  1. Store - the printed ZPL is written as a file into a storage connection.
  2. Notify - a message goes to a broker; the same protocols as on the receiving side are supported.
  3. Trigger - an HTTP POST to your own URL, HMAC-SHA256 signed like every other webhook.

The file name

The key is built from a template. All times are UTC, placeholders without a value are dropped.

PlaceholderMeaning
{yyyy} {MM} {dd}year, month, day
{HH} {mm} {ss}hour, minute, second
{subscription}name of the subscription
{printer}target printer
{key}key of the bus message
{id}unique id per label
Template: {yyyy}/{MM}/{dd}/{subscription}/{id}.zpl
Result:   2026/09/09/Outbound/8f3c1e2a9b7d4c0e.zpl

What the notification carries

The message and the webhook receive the same payload. The ZPL itself is only included when you ask for it - otherwise a graphics-heavy file blows past every message size limit.

{
  "schemaVersion": 1,
  "event": "label.printed",
  "utc": "2026-09-09T12:34:56.7890000Z",
  "subscriptionId": 12,
  "subscription": "Outbound",
  "topic": "orders.shipped",
  "printer": "rp:7",
  "messageKey": null,
  "designId": 42,
  "storageKey": "zpl/2026/09/09/Outbound/8f3c1e2a.zpl",
  "storageUrl": "https://archive.s3.eu-central-1.amazonaws.com/zpl/2026/09/09/...",
  "zplBytes": 812,
  "zpl": null
}

The webhook carries the same headers as the regular webhook endpoints: X-ZplCloud-Event, X-ZplCloud-Delivery, X-ZplCloud-Timestamp and, once a secret is stored, X-ZplCloud-Signature with the HMAC-SHA256 over the raw body.

Trying it without hardware

You do not need a real printer to test any of this: a virtual test printer accepts ZPL like a device and drops the rendered label into its own history - no render quota, watermarked output. It sits in the "Sandbox printers" tab under Printers and is selectable in every "send ZPL" dialog, so a whole subscription including its output target can be rehearsed before any paper moves.