Skip to content

Identifier bindings

A binding pairs a scanned value (raw) with the thing it stands for (target), under one scan scheme: a cartridge’s tag with the material lot it carries, a plate with the printer it is aboard. Use one when the carrier holds a code you did not print: a factory barcode, a tag’s EPC, a reusable plate.

A raw has one live target at a time. A target holds any number of raws, or exactly one when its scheme sets identifier.exclusiveTarget. Retired bindings stay as history, so a raw’s past reads back newest first.

A binding stores the target string. What the target means (a lot’s material and remaining quantity, a printer’s state) stays on your own rows.

Binding and retiring take a role manager, or a role the scheme names in identifier.binders. A scheme declared at boot carries the same identifier; a code-owned scheme applies what its code declares on every boot, and a db-owned one is seeded with it once:

index.ts
scanSchemes: [
{
version: 22,
name: 'Plate',
target_facet: 'plateCode',
encoding: 'delimited',
identifier: { binders: ['print-servicing'], exclusiveTarget: true },
},
],

Any signed-in caller reads bindings. Each binding records bound_by (the user, when the caller is one) and bound_via: api, scan or workflow. A scheme save that leaves identifier out keeps the stored settings, and null clears them.

OperationWhat it does
bindPairs a raw with a target. A raw bound to another target answers 409; the same pair again answers 200 with the standing binding. Under an exclusive scheme, the target’s old raw is retired in the same transaction
rebindMoves a raw to a new target: its old binding (and, under an exclusive scheme, the target’s old raw) is retired and the new one inserted in one transaction
retireEnds one live binding. It stays as history, and the raw is free to bind again
resolveA raw’s live binding, if it has one
listA scheme’s live bindings, by target or raw, or every binding with includeRetired

Partial unique indexes decide concurrent binds: of two binds of one raw, or two onto one exclusive target, exactly one lands.

await lt.identifiers.bind({ scheme: 22, raw: 'PLT-0193', target: 'printer-04' });
const { data } = await lt.identifiers.listBindings({ scheme: 22, raw: 'PLT-0193' });

The same operations are on HTTP under /api/identifiers/:scheme (reference), the SDK as lt.identifiers (reference), and MCP as list_identifier_bindings, read-safe, where raw answers one value’s live target.

A bind scan step binds the scanned value to the held item. Hold the item first with a hold step, then scan the value under a rule whose step binds:

{ "verb": "bind", "subject": { "schemes": [21] }, "params": { "bind": { "facet": "serialNumber" } },
"done": { "markdown": "**{scan.target}** is on {subject.serialNumber}." } }

The binding is kept under params.bind.scheme, else the scanned code’s scheme, and binds to the held row’s params.bind.facet, else its scheme’s target facet. The acting identity must be a binder. A value bound to something else is refused and the held item is kept, so the right value can be scanned next.

Two options let one scan finish a hand-off, as when a plate is loaded onto a printer:

  • move: true moves a value bound elsewhere to this target, retiring its old binding in the same transaction (a rebind).
  • resolve: { payload? } resolves the held row once the value is bound, with the payload rendered from scan templates ({scan.target} is the bound value, {subject.<facet>} reads the held row), and releases it. The resolve runs through the resolve verb’s guarded statement, so the row’s write scope, claim and form apply.
{ "verb": "bind", "subject": { "schemes": [21] },
"params": { "bind": { "move": true, "resolve": { "payload": { "plateId": "{scan.target}" } } } },
"done": { "markdown": "**{scan.target}** is on {subject.serialNumber}." } }

The binding is written first and the row resolved after it, in two statements. A refused binding resolves nothing. A held row that has already ended answers as done and the binding stands, and scanning again answers the binding as already in place.