High-speed label printing needs coupling that keeps up. This guide uses a bank of pre-allocated Zimarkers plus asynchronous coupling so nothing blocks the printing process:
- A background process keeps a local bank of Zimarkers full, so codes are always available on-site.
- When a label is printed, you couple its code to a pallet with a fire-and-forget async call, either right away or from an offline queue that drains when connected.
- A webhook reports back afterwards, so you only act on the couplings that failed.
For the concepts behind banks, allocation, and sync vs. async, see the Coupling API overview.
- An API key (
ZIM.…). See Authentication. - A webhook endpoint to receive coupling results. See Webhook Setup.
Run this in a background process, not in the print loop, typically a small Node.js, Python, or .NET service triggered by a cron job. Allocate a batch of Zimarkers ahead of time and store them locally. Each allocated code comes with its printable image, so once they're banked you can print without any live call.
POST /integration/rest/api/v1/zmarker-code/allocate?quantity=50
curl -X POST "https://api.zimark.link/integration/rest/api/v1/zmarker-code/allocate?quantity=50" \
-H "X-AUTH-KEY: ZIM.your-api-key-here"The batch is reserved atomically: all-or-nothing. 200 OK returns a list of allocated codes:
[
{ "markerCode": "68", "markerImage": "iVBORw0KGgoAAA…" },
{ "markerCode": "69", "markerImage": "iVBORw0KGgoAAA…" }
]markerCode: the Zimarker code, reserved for you and never reused.markerImage: the marker as a base64-encoded PNG, ready to print. (Add?imageDelivery=URLto get amarkerImageUrlinstead.)
The filler itself is a small loop: check how many unused codes are left locally, and if that count is below a threshold, allocate a batch to refill. Run it on a timer or trigger it whenever the bank drops below a low-water mark. Size the batch so it comfortably covers what you print between checks, and the bank never runs dry mid-run.
Two things keep it reliable:
- Allocation is all-or-nothing. A failed batch allocates nothing, so treat a
400as "refill later" and retry. Never assume a partial batch. - Allocated codes are consumed immediately. From Zimark's side a code is spent the moment it's allocated, so your local store is the source of truth for what's still unused. Persist it durably. Codes lost from the bank can't be reclaimed.
When you print a label, take the next code from your local bank and couple it to the pallet's WMS ID. Use async=true: the call validates up front, returns immediately with an operation identifier, and finishes coupling in the background. Your printer never waits.
POST /integration/rest/api/v1/track/couple?async=true
curl -X POST "https://api.zimark.link/integration/rest/api/v1/track/couple?async=true" \
-H "X-AUTH-KEY: ZIM.your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"markerCode": "68",
"wmsId": "LPN00012345"
}'| Field | Type | Required | Notes |
|---|---|---|---|
markerCode | string | Yes | The Zimarker code from your bank. |
wmsId | string | Yes | The pallet's WMS ID. |
customFields | array | No | { fieldKey, fieldValue } pairs to set on the pallet. |
202 Accepted returns an operation identifier. Coupling then completes in the background:
{
"operationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Because the response comes back before coupling finishes, print the label and move on. The 202 means the request was accepted and validated, not that coupling succeeded. That outcome arrives on the webhook.
Every async coupling reports its outcome on the coupling-events webhook: COUPLING_SUCCEEDED or COUPLING_FAILED. Since you fire and forget, this is where you catch the ones that didn't take: a duplicate WMS ID, an already-coupled marker, or a validation error. Match the event back to your request by operationId.
The exact payload isn't part of the OpenAPI spec yet; the shape below is illustrative. Confirm the field names against a live delivery before relying on them.
{
"category": "COUPLING",
"eventIdentifier": "evt-9f3c…",
"triggerName": "COUPLING_FAILED",
"triggerTime": 1783164318353,
"payload": {
"operationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"markerCode": "68",
"wmsId": "LPN00012345",
"reason": "DUPLICATE_WMS_ID"
}
}Handle it like any other Zimark webhook. See Webhook Reference for the envelope and idempotency rules. In practice:
COUPLING_SUCCEEDED: nothing to do; the pallet is coupled.COUPLING_FAILED: the label is printed but not linked. Re-couple: draw a fresh code from the bank and callPOST /track/coupleagain for thatwmsId, or flag the pallet for manual handling.
- Step 1 returned a batch of codes now sitting in your local bank.
- Step 2 returned
202with anoperationIdfor each label, and printing didn't wait. - Your webhook receives one
COUPLING_SUCCEEDEDorCOUPLING_FAILEDper operation, and failures are re-coupled.
If a call fails synchronously, check Troubleshooting. A 400 on allocation means the batch exceeds the maximum size or there isn't enough produced inventory; a 400 on couple means the code is invalid, already coupled, or the WMS ID is a duplicate.
- Coupling API: the concepts behind allocation, coupling, banks, and sync vs. async.
- Create a Zimarker: the synchronous, one-at-a-time version.