A scan is a rule
scanSchemesPOST /api/scan-codes/executeexecute_scan_codeupsert_scan_ruleA code runs an ordered list of queue queries and the first match acts, so one scan both moves the item and reports where it is.
The situation
A carton reaches a bench carrying one printed code. What the scan should do depends on where the carton’s row sits right now: waiting to be collected, already collected, or in some other queue. The associate should not have to know which.
The recipe
A scan code reads version:category:target. The version picks the scheme, the category picks the rule, and the rule is an ordered list of steps over the escalation queue: event, condition, action.
scanSchemes: [{ version: 10, name: 'Carton serial', target_facet: 'serialNumber', rules: [{ category: '1', name: 'Collect', steps: [ { query: { roles: ['packing'], status: 'pending', availability: 'available', facets: { state: 'packed' } }, verb: 'resolve', params: { resolverPayload: { outcome: 'collected' } } }, { query: { roles: ['packing', 'shipping'] }, verb: 'show-detail' }, ], fallback: { markdown: '**No row for this serial.** Register the carton first.' }, }],}],Scanning 10:1:SN-123 walks the steps. The first step whose query matches runs its verb. Put the expected state first and a broad show-detail last: a carton in the wrong queue still answers the scan with where its row actually is. With no match, the station shows the fallback.
The same code runs from a keyboard-wedge scanner, the dashboard header bar, POST /api/scan-codes/execute with { "code": "10:1:SN-123" }, or the execute_scan_code MCP tool.
The guarantee
Claim, resolve and cancel steps ride single-statement by-metadata operations, with the caller’s role scope folded into the same SQL filter. Two people scanning the same code at once produce exactly one transition: the second scan reports conflict or falls through to the locator step. Every terminal state is a structured 200 outcome (executed, no_match_fallback, conflict, and so on). See Scan codes.
Related patterns
The physical world
The label and the row share a facet
A scheme names the metadata facet a label's target matches, so a printed code finds its row by the same value the workflow stamped.
The physical world
One scan, one action
When a step offers exactly one choice, the scan runs it directly: the item that just arrived is claimed and its form is on screen.
The physical world
Match and refuse before any write
A wrong pairing is refused with copy that names the right container, and nothing is written.
The physical world
Facts above, choices below
One lifelong code serves many intents: the station states what is true about the row and offers the actions that fit.
