Rendering, Anreicherung & Batch-Jobs
api.zplcloud.com mit API-Key.
Authentifizierung (Header X-API-Key oder Basic Auth), Fehlerformat, der Header X-ZplCloud-Request-Id und der Webhook-Callback sind für alle Endpunkte gleich und in der API-Übersicht beschrieben. Alle Endpunkte dieser Seite außer dem älteren ZPL-Renderpfad akzeptieren die optionalen Query-Parameter webhookId=<id> und webhookResult=true|false; das Event steht jeweils beim Endpunkt (Payload und Signatur).
Design-IDs
Design-Endpunkte adressieren ein gespeichertes Design als <name>.<id>: das beim Speichern vergebene Design-Tag (Kleinbuchstaben, Ziffern, _ und -) und die numerische Design-ID, getrennt durch den letzten Punkt - zum Beispiel shipping-label.42.
- Wo steht sie: Nach Speichern im Designer zeigen der Speichern-Dialog und die Statusleiste die vollständige ID (shipping-label.42). Dieselbe ID gilt für ZPL, PDF, PNG und Batch-Jobs.
- Scope: Das Design muss zur Firma des API-Key-Besitzers gehören (Keys ohne Firma: zu dessen E-Mail). Name und Nummer müssen beide passen - sonst
404 Design "shipping-label" not found. - Formatfehler:
400 Invalid design ID - expected format "<name>.<id>" (e.g. my-label.42). - Datensätze: ein JSON-Array mit einem Objekt je Etikett; die Schlüssel sind die Binding-Namen im Design, z. B.
[{"sku":"A-1001","qty":"2"}].
Design als PDF rendern
Eine PDF-Seite je Datensatz in Labelgröße und Auflösung des Designs. Ohne Body (oder mit []) wird das Design einmal mit seinem eigenen Inhalt gerendert. Das Kontingent wird vor dem Rendern geprüft.
| Parameter | Typ | Hinweise |
|---|---|---|
designId (Pfad) | string | <name>.<id> |
| Body | array | Optional. Datensätze, max. 2.000 je Aufruf und 5 MB. Größere Mengen: Batch-Jobs. |
webhookId, webhookResult | Query | Optionaler Webhook-Callback, Event api.design.pdf. |
| Status | Fehler |
|---|---|
| 400 | Ungültige Design-ID, ungültiges JSON, PDF rendering failed: …, ungültige webhookId. |
| 401 | API-Key fehlt oder ist ungültig. |
| 404 | Design nicht gefunden (oder nicht im Scope des Key-Besitzers). |
| 413 | Body über 5 MB oder mehr als 2.000 Datensätze. |
| 429 | Monatliches Label-Kontingent verbraucht, siehe Kontingent. |
Renders: einer je Seite. Webhook api.design.pdf, Summary { design, format: "pdf", pages, bytes, widthMm, heightMm, dpi }; mit webhookResult=true kommt das PDF als Base64 mit.
Design als PNG rendern
Ein Etikett als PNG. Der Body ist dasselbe optionale Datensatz-Array; record wählt den Datensatz über den nullbasierten Index.
| Parameter | Typ | Standard | Hinweise |
|---|---|---|---|
designId (Pfad) | string | - | <name>.<id> |
record (Query) | int | 0 | Index im Datensatz-Array. Außerhalb: 400 record must be between 0 and n. |
| Body | array | - | Optionale Datensätze, max. 5 MB. |
webhookId, webhookResult | Query | - | Optional, Event api.design.png. |
Antwort: 200 image/png, Dateiname <name>.<id>.png. Fehler: 400, 401, 404, 413 und 429 wie beim PDF. Renders: 1. Webhook api.design.png, Summary { design, format: "png", record, bytes, widthMm, heightMm, dpi }.
Design als ZPL rendern
Der bestehende ZPL-Pfad: gleiche Design-ID und gleiches Datensatz-Array, Antwort application/x-zpl; charset=utf-8 mit einem ^XA…^XZ-Block je Datensatz. Grenzen: 500 Datensätze, 1 MB Body. Wie PDF und PNG wird er gegen das monatliche Label-Kontingent geprüft: ein Render je Datensatz (mindestens 1), 429 { error, hint, limit, used, requested }, wenn das Kontingent aufgebraucht ist. Er erscheint zusätzlich in der API-Statistik und hat keinen Webhook-Callback. Für PDF-Ausgabe, Druck oder mehr Datensätze die Endpunkte oben (jeder Tarif) oder einen Batch-Job (Pro-Tarif) verwenden.
Label-Anreicherung
Ergänzt jedes Etikett in fremdem ZPL - Amazon, UPS, FedEx, DHL, USPS - um eigene Textfelder: SKU, Lagerplatz, Menge, Bestellnummer, freier Text. Die Original-Kommandos bleiben byte-identisch; die Overlay-Kommandos werden je Etikett direkt vor dessen abschließendem ^XZ eingefügt. Der Carrier-Barcode wird nie angefasst.
Feature-Schalter
Den Endpunkt gibt es nur, solange die Label-Anreicherung in zplCloud aktiviert ist. Sonst antwortet er mit 404 Label enrichment is not available.
Pro-Tarif
Aufrufe der Label-Anreicherung setzen den Pro-Tarif des Key-Besitzers voraus. Andere Tarife erhalten 403, bevor der Body gelesen wird - ein angeforderter Webhook-Callback meldet den Fehler. Ist der Tarif nicht lesbar, lautet die Antwort 503. Die Antworttexte sind wie überall in der API Englisch:
Request-Body
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
zpl | string | Pflicht | Carrier- oder Amazon-ZPL mit einem oder mehreren ^XA…^XZ-Etiketten, max. 4.000.000 Zeichen. |
dpi | int | 203 | Auflösung dieses ZPL: 203, 300 oder 600. Rechnet Millimeter in Dots um. |
fields | array | Pflicht | 1 bis 50 Overlay-Felder (Tabelle unten). |
records | array | - | Objekte Spalte → Wert, max. 10.000. Werte müssen JSON-Strings sein. |
clearZone | bool | false | Zuerst eine weiße Fläche zeichnen, damit Overlays auf bedruckten Bereichen lesbar bleiben. |
zoneX, zoneY, zoneW, zoneH | mm | 0 | Position und Größe der weißen Fläche; nur wenn zoneW und zoneH größer 0 sind. |
| Feld-Eigenschaft | Typ | Standard | Hinweise |
|---|---|---|---|
x, y | mm | - | Linke obere Ecke des Textblocks (^FO). |
w | mm | - | Breite des Textblocks (^FB), mindestens eine Zeichenhöhe. |
h | mm | - | Höhe. Zeilen im Block = h ÷ Schrifthöhe, abgerundet, mindestens 1. |
fontPt | number | 9 | Schriftgröße in Punkt, skalierbare Schrift ^A0; Dots = fontPt × dpi / 72 (min. 6). |
align | string | L | L, C oder R innerhalb von w. |
column | string | - | Wert aus dem Datensatz des Etiketts. Schlüssel ohne Beachtung der Groß-/Kleinschreibung, ein führendes $ wird ignoriert; fehlt der Schlüssel, wird nichts gedruckt. |
text | string | - | Statischer Text; gilt, wenn keine column gesetzt ist oder das Etikett keinen Datensatz hat. |
label | string | - | Optionales Präfix, gedruckt als label: Wert. |
Felder mit leerem Wert werden übersprungen. Text geht als UTF-8 (^CI28) raus; ^ und ~ in Werten werden per ^FH hex-kodiert, Daten können also keine Kommandos einschleusen.
Zuordnung Datensatz → Etikett
- Das ZPL wird in seine
^XA…^XZ-Blöcke geteilt; Datensatz n gehört zu Etikett n (beide ab 0, in Reihenfolge). - Genau ein Datensatz: Er gilt für alle Etiketten (z. B. dieselbe Bestellnummer auf allen Paketen einer Sendung).
- Keine Datensätze: Nur
text-Felder werden gedruckt, auf jedem Etikett. - Weniger Datensätze als Etiketten: Die übrigen Etiketten bekommen nur ihren statischen
text. Überzählige Datensätze werden ignoriert. - Alles zwischen den Blöcken bleibt erhalten. ZPL ohne
^XA…^XZ-Block kommt unverändert mitlabels: 0zurück.
Antwort: JSON { "zpl": "…", "labels": 2 }; mit ?format=zpl das rohe ZPL als application/x-zpl; charset=utf-8, direkt druckbar. Renders: einer je Etikett (mindestens 1), nie durch das Kontingent blockiert. Webhook api.enrich.completed, Summary { labels, fields, records, dpi, zplBytes }.
| Status | Fehler |
|---|---|
| 400 | zpl is required …, fields is required …, Too many fields (max 50)., dpi must be 203, 300 or 600., format must be json (default) or zpl., ungültiges JSON (z. B. eine Zahl statt eines Strings in records). |
| 403 | Label enrichment calls require the Pro plan. - der Key-Besitzer hat nicht den Pro-Tarif (Body mit plan und hint). |
| 404 | Label-Anreicherung ist nicht aktiviert. |
| 413 | zpl über 4.000.000 Zeichen, mehr als 10.000 Datensätze oder Body über 24 MB. |
| 503 | Der Tarif konnte nicht geprüft werden (Datenbank) - später erneut versuchen. |
Beispiel: SKU und Lagerplatz auf dem Carrier-Label
Ein 4 × 6 Zoll großes Carrier-Label (101,6 × 152,4 mm, 203 dpi) bekommt unten auf einem weißen Streifen links die SKU und rechts den Lagerplatz:
Jedes Etikett erhält vor seinem ^XZ ^FO40,1111^A0N,39,39^FB480,1,0,L,^CI28^FDSKU: A-1001^FS und das rechtsbündige Lagerplatz-Feld.
Batch-Jobs
Rendert ein gespeichertes Design mit vielen Datensätzen in einem Aufruf: Ausgabe als ZPL oder PDF, optional eine Pickliste als PDF und optional der Druck. Standardmäßig synchron; "async": true antwortet sofort und ruft den Webhook, wenn der Job fertig ist.
Pro-Tarif
Batch-Jobs - auch mit Pickliste und Druck - setzen den Pro-Tarif des Key-Besitzers voraus. Andere Tarife erhalten 403, bevor der Body gelesen wird - ein angeforderter Webhook-Callback meldet den Fehler. Ist der Tarif nicht lesbar, lautet die Antwort 503. Job-Status, Ausgabe und Pickliste (GET /v1/batch/jobs/…) werden nicht auf den Tarif geprüft; Jobs gibt es nur, wenn sie im Pro-Tarif gestartet wurden. In anderen Tarifen je Aufruf bis zu 500 Datensätze als ZPL oder 2.000 als PDF rendern.
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
records | array | Pflicht | 1 bis 10.000 Objekte, eines je Etikett (PDF-Ausgabe: max. 5.000). Werte dürfen Strings, Zahlen oder Booleans sein. |
output | string | zpl | zpl, pdf oder none. none braucht einen printer oder eine pickList. |
printer | string | - | Druckziel wl:{id}, Weblink-Seriennummer, rp:{id} oder vp:{id}, siehe Drucken. |
pickList | object | - | Pickliste als PDF aus denselben Datensätzen, siehe Pickliste. |
async | bool | false | true: 202 mit jobId, der Job läuft im Hintergrund. |
Body max. 32 MB. Vor dem Rendern wird in dieser Reihenfolge geprüft: Tarif (Pro), Body, Datensätze, Ausgabe, Design, Kontingent (Anzahl Datensätze), Druckziel - ein Tippfehler beim Drucker fällt also sofort auf. Query-Parameter webhookId / webhookResult: Event api.batch.completed.
| Ziel | Drucker | Zustellung |
|---|---|---|
wl:{id} oder Seriennummer | Weblink-Cloud-Drucker | Über die Weblink-Verbindung des Druckers. Muss für den Key-Besitzer sichtbar sein; ein gesperrter Drucker antwortet mit 409. |
rp:{id} | Remote-Drucker | Über den zplCloud-CLI-Agenten (zplcloud proxy) im eigenen Netz; der Agent muss online sein. |
vp:{id} | Virtueller Drucker | Landet in der Historie des virtuellen Druckers; max. 512 KB je Block. Nur wenn virtuelle Drucker aktiviert sind. |
Die Ziele liefern GET /v1/printers (an den Key gebundene Drucker) und GET /v1/company/printers (alle Drucker der Firma) - verwendet wird das Feld target eines Eintrags. Etiketten gehen in Blöcken zu je 100 raus, eine Anfrage je Block. Der erste fehlgeschlagene Block stoppt den Druck: Der Job endet mit print_failed, print.error nennt den Grund, sentChunks / sentLabels zeigen den Fortschritt. Ausgabe und Pickliste werden trotzdem geliefert; ein print_failed-Job antwortet mit HTTP 200 und zählt alle Datensätze.
Fehler beim Ziel: 400 Invalid remote printer target - expected rp:{id}. (analog für vp: und wl:), 400 Virtual printers are not available., 404 Printer not found. / Remote printer not found. / Virtual printer not found., 409 The printer is blocked.
Pickliste
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
title | string | Design-ID | Überschrift auf Seite 1. |
subtitle | string | Datum, Uhrzeit, Zeilen | Standard: 2026-09-11 10:15 UTC · 250 Positionen. |
columns | string[] | alle Schlüssel | Spalten in dieser Reihenfolge; Standard sind alle Schlüssel des ersten Datensatzes. Max. 20. |
groupBy | string | - | Gruppiert die Zeilen nach dieser Spalte (Gruppenzeile Spalte: Wert), Gruppen sortiert. |
sortBy | string | - | Sortiert die Zeilen (innerhalb der Gruppen) natürlich: A-7 vor A-10. |
checkbox | bool | true | Ankreuzkästchen am Anfang jeder Zeile. |
paper | string | A4 | A4 oder Letter. |
Nur verfügbar, wenn Picklisten aktiviert sind (sonst 400 Pick lists are not available.). Eine Pickliste fasst höchstens 5.000 Datensätze; Werte, die keine Strings sind, erscheinen als JSON-Text.
Synchrone Antwort
jobIdentspricht dem HeaderX-ZplCloud-Request-Id.statusistsucceededoderprint_failed.outputist beinonenull; PDF-Ausgabe kommt alsbase64.pickListundprintsindnull, wenn nicht angefordert.outputUrlistnull, wenn die Ausgabe größer als 20 MB ist (sie steht dann nur in dieser Antwort).- Renderfehler:
400 { "error": "Rendering failed: …", "jobId": "…", "requestId": "…" }.
Asynchrone Jobs
- Alle Prüfungen (Tarif, Datensätze, Design, Kontingent, Druckziel) laufen vor dem 202; diese Fehler kommen also weiterhin direkt zurück.
- Je Server laufen höchstens zwei Batch-Jobs gleichzeitig; weitere warten als
queued. - Ist der Job fertig, wird der Webhook mit
api.batch.completedaufgerufen;requestIdist diejobId. MitwebhookResult=trueenthältresultdie komplette synchrone Antwort als JSON (bis 10 MB). - Der
statusim Payload folgt dem HTTP-Ergebnis: Einprint_failed-Job kommt als"status": "succeeded"mitsummary.jobStatus: "print_failed"undsummary.printErroran. Maßgeblich istsummary.jobStatus. - Ohne
webhookIddiestatusUrlabfragen. Renders werden am Ende gezählt, und nur, wenn der Job nicht fehlgeschlagen ist. - Ein Server-Neustart bricht laufende Jobs ab:
failed,The job was cancelled (server restart).
| Status | Bedeutung |
|---|---|
queued | Angenommen, wartet auf einen freien Platz. |
running | Ausgabe und Pickliste werden erzeugt, danach wird gedruckt. |
succeeded | Fertig; alle Blöcke wurden vom Drucker angenommen (falls einer angegeben war). |
print_failed | Ausgabe und Pickliste sind fertig, aber ein Druckblock ist fehlgeschlagen (print.error). |
failed | Rendern fehlgeschlagen oder Job abgebrochen (error). |
| Grenze | Wert |
|---|---|
| Datensätze je Job | 10.000 (PDF-Ausgabe 5.000, Pickliste 5.000) |
| Request-Body | 32 MB |
| Druckblock | 100 Etiketten |
| Parallele Jobs | 2 je Server, weitere warten |
| Gespeicherte Ausgabe / Aufbewahrung | 20 MB / 1 Stunde |
Komplettbeispiel: asynchroner Job mit Druck auf rp:12
1. Druckziel ermitteln:
2. Job mit Webhook 12 starten (GET /v1/webhooks listet die IDs; der Key-Besitzer braucht den Pro-Tarif):
3. Der Aufruf antwortet sofort mit 202 und der jobId (siehe oben). 4. Hat der letzte Block den Drucker erreicht, erhält die Webhook-URL einen POST mit den Headern X-ZplCloud-Event: api.batch.completed, X-ZplCloud-Delivery, X-ZplCloud-Timestamp und X-ZplCloud-Signature:
5. Die Pickliste innerhalb der Stunde abholen: GET /v1/batch/jobs/3f0c9a7e-…/picklist.
Job-Status und Downloads
| Route | Antwort | Webhook-Event |
|---|---|---|
| GET /v1/batch/jobs/{jobId} | Status als JSON | api.batch.status |
| GET /v1/batch/jobs/{jobId}/output | ZPL (application/x-zpl) oder PDF, bis 20 MB | api.batch.output |
| GET /v1/batch/jobs/{jobId}/picklist | Pickliste als PDF | api.batch.picklist |
- Jobs liegen 1 Stunde im Speicher (
expiresUtc) des Servers, der sie angenommen hat. Danach oder auf einer anderen Server-Instanz:404 Batch job not found. Jobs are kept for 1 hour on the server that accepted them. - Einen Job sieht nur dieselbe Firma (Keys ohne Firma: dieselbe E-Mail).
/outputantwortet mit 404, solange der Job läuft, beioutput: noneund bei Ausgaben über 20 MB;/picklistmit 404, solange er läuft oder ohnepickList.- Status und Downloads zählen keine Renders und werden nicht auf den Tarif geprüft.
Kontingent und Render-Zählung
| Endpunkt | Geprüft (429) | Gezählte Renders |
|---|---|---|
| POST /v1/zpl/render/design/{designId} | ja | einer je Datensatz (mindestens 1) |
| POST /v1/pdf/render/design/{designId} | ja | einer je Seite (Datensätze, mindestens 1) |
| POST /v1/png/render/design/{designId} | ja | 1 |
| POST /v1/batch/design/{designId} | ja, Anzahl Datensätze | einer je Datensatz, auch bei print_failed |
| POST /v1/enrich/zpl | nein, nie blockiert | einer je Etikett (mindestens 1) |
| GET /v1/batch/jobs/… | nein | 0 |
Batch-Jobs und Label-Anreicherung setzen zusätzlich den Pro-Tarif voraus (403, siehe oben). Fehlgeschlagene Aufrufe (HTTP 400 und höher) zählen nichts. Ist das monatliche Label-Kontingent verbraucht, antworten die geprüften Endpunkte, bevor gearbeitet wird:
Tarifgrenzen, Top-ups, gepufferte Zählung und GET /v1/quota: API-Übersicht → Kontingent. Auch die ZPL-Tools-API wird nie blockiert.