# Couple Zimarkers for high-speed or offline printing 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: 1. A background process keeps a local bank of Zimarkers full, so codes are always available on-site. 2. 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. 3. 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](/zimarkers/coupling-api) overview. ## Prerequisites - An API key (`ZIM.…`). See [Authentication](/rest-api/authentication). - A webhook endpoint to receive coupling results. See [Webhook Setup](/webhooks/management). ## Step 1: Keep the bank filled (background) 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`](/api#operation/allocate) cURL ```bash 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" ``` JavaScript ```javascript const response = await fetch( 'https://api.zimark.link/integration/rest/api/v1/zmarker-code/allocate?quantity=50', { method: 'POST', headers: { 'X-AUTH-KEY': 'ZIM.your-api-key-here' } } ); const codes = await response.json(); // Store each { markerCode, markerImage } in your local bank saveToBank(codes); ``` Python ```python import requests response = requests.post( 'https://api.zimark.link/integration/rest/api/v1/zmarker-code/allocate', headers={'X-AUTH-KEY': 'ZIM.your-api-key-here'}, params={'quantity': 50} ) codes = response.json() # Store each { markerCode, markerImage } in your local bank save_to_bank(codes) ``` The batch is reserved atomically: all-or-nothing. `200 OK` returns a list of allocated codes: ```json [ { "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=URL` to get a `markerImageUrl` instead.) 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 `400` as "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. ## Step 2: Couple at print time (asynchronous) 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`](/api#operation/couple) cURL ```bash 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" }' ``` JavaScript ```javascript const response = await fetch( 'https://api.zimark.link/integration/rest/api/v1/track/couple?async=true', { method: 'POST', headers: { 'X-AUTH-KEY': 'ZIM.your-api-key-here', 'Content-Type': 'application/json' }, body: JSON.stringify({ markerCode: '68', wmsId: 'LPN00012345' }) } ); const { operationId } = await response.json(); // Keep operationId so you can reconcile the webhook result later ``` Python ```python import requests response = requests.post( 'https://api.zimark.link/integration/rest/api/v1/track/couple?async=true', headers={ 'X-AUTH-KEY': 'ZIM.your-api-key-here', 'Content-Type': 'application/json' }, json={'markerCode': '68', 'wmsId': 'LPN00012345'} ) operation_id = response.json()['operationId'] # Keep operation_id so you can reconcile the webhook result later ``` ### Request fields | 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: ```json { "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. ## Step 3: Reconcile failures via 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. ```json { "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](/webhooks/webhooks) 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 call `POST /track/couple` again for that `wmsId`, or flag the pallet for manual handling. ## Confirm it worked - Step 1 returned a batch of codes now sitting in your local bank. - Step 2 returned `202` with an `operationId` for each label, and printing didn't wait. - Your webhook receives one `COUPLING_SUCCEEDED` or `COUPLING_FAILED` per operation, and failures are re-coupled. If a call fails synchronously, check [Troubleshooting](/rest-api/troubleshoot). 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. ## Related - [Coupling API](/zimarkers/coupling-api): the concepts behind allocation, coupling, banks, and sync vs. async. - [Create a Zimarker](/guides/sync-coupling): the synchronous, one-at-a-time version.