/v1/qr-tags/resolve
Resolve scanned QR tags
Turns the text read from one or two QR codes into what a scanning app should show: each tag's target (a printer with its live status, a slot with what is assigned to it and whether the printer sees a spool, a filament variant, a spool with its weight and the slot it is loaded in, or a blank tag with its code), every action the scan offers, and which of those the scanner can run right now. Needs qr.scan and the QR Workflows add-on. Each call counts as a scan on every tag it finds (scanCount, lastScannedAt). A blank isn't saved until it is set up, so scanning one writes nothing.
mode says how to read the result. Every mode other than single comes with a message you can show as is:
| mode | When |
|---|---|
single |
One valid tag. actions lists what it offers. |
pair |
Two tags with actions together: a variant with a printer or a slot (assign), two slots (move, or swap when both hold filament), a spool with a printer or a slot (load it, moving it out of the slot it is in), or a blank with a printer, slot, variant or spool (set the blank up for it; with a variant the default registers a new spool). |
invalid_pair |
Two tags with nothing to do together: two printers, two variants, two spools, two blanks, a spool with a variant, a printer with a slot, the same slot twice, two slots with nothing assigned, or a spool with the slot it is already in ("Jade White is already in A1/2 on X1C-01."). |
unknown_tag |
A Printago payload for this store whose tag doesn't exist. A blank's payload is never one: any blank whose ID is 1 to 24 letters, digits, - or _ resolves as a blank until it is set up. |
revoked |
The tag was revoked. tags still describes it. |
orphaned |
The tag's printer (or a slot tag's printer, a variant or a spool) was deleted. The tag's status becomes orphaned. |
other_store |
The payload belongs to another store. It is not looked up. |
unsupported_version |
The payload was made by a newer Printago (v above 1). Ask the user to update. |
Text that isn't a Printago payload at all (a link to another page, a Wi-Fi code, JSON without a numeric v) is a 400 with the message "This QR code isn't a Printago tag." Scanning the same tag twice in one request counts as one tag. The payload format is described under POST /v1/qr-tags.
Each action has available, and when it is false, a reasonCode and a reason such as "Printer is offline", "Printer is printing", "No jobs are waiting in the queue" or "You don't have permission". When a printer's live status can't be read within 3 seconds, the printer's statusLabel is "Status unavailable" and the actions that need its live status have reasonCode status_unknown; resolve again to retry. Actions that only change Printago's records, such as loading a spool into a slot, stay available. At most one action has default: true: for a printer, Confirm ready while it is waiting for confirmation, Resume while it is paused, otherwise Open printer; for a slot, Assign filament; for a variant, Assign to a printer; for a pair, its one action, whose label names the outcome ("Move PLA Basic Red to A2/1", "Swap A1/3 and S1"). Destructive actions (Stop, Disable, Clear all slots) are never the default. Actions with confirm: true carry a confirmMessage to ask before running them. Actions whose runs is app (Open printer) are handled by the client and are never sent to POST /v1/qr-tags/execute.
A slot tag's display has slot: ref (A1/3, S1), assigned (the filament's name, brand and color, plus instanceId and remainingGrams when a spool is loaded there, or null), loaded (the printer reports a spool there) and exists (false while the printer doesn't report the slot, for example with its AMS unplugged; its actions are then unavailable). A variant tag's display has variant with its name, material, brand, type and color. A spool tag's display has spool: its variant, remainingGrams, nominalGrams, labelCode, empty (no filament left) and location (printerId, printerName, amsIndex, slotIndex, ref of the slot it is loaded in, or null). A blank tag's display has blank with its code.
A tag's record, not its printed payload, says what it is: a blank that was set up resolves as the printer, slot, variant or spool it was set up for, though its QR code still says "t": "blank".
Setting a blank up without scanning a second tag
A blank's payload is a link (see POST /v1/qr-tags); send it as it was read, or the JSON of a blank printed before blank payloads were links. Either resolves the same. To set a blank up for something that has no tag to scan, as the blank's page on Printago on the web does, send the blank's payload alone with a target, shaped like a target of POST /v1/qr-tags: {"entityType": "printer", "entityId"}, {"entityType": "printer_slot", "entityId", "amsIndex", "slotIndex"}, {"entityType": "material_variant", "entityId"} or {"entityType": "material_instance", "entityId"}. The blank and the target resolve as the pair they would be when scanned together, with the same actions, main button and settings; tags lists only the blank. Send refresh: true so choosing a target doesn't count another scan. A target that isn't in the store is a 404 with code: "target_not_found", and a target with two payloads is a 400. Once the tag isn't a blank anymore the target is ignored, and the response says what the tag is for now. Run the chosen action with POST /v1/qr-tags/execute, passing the same target.
Spool actions: on its own a spool offers Update grams (the default), Remove from its slot (when loaded), Load into a printer, Mark empty (asks first; it also clears the spool's slot) and Open printer. A variant on its own also offers Register spool. With a printer that has one slot, a spool's load needs no choice and its label names the slot ("Load into S1"); with more slots it asks for one. A load that moves a spool out of another slot says so in label ("Move Jade White to A1/3") and in detail, a second line to show under the button ("Takes it out of A1/2 · X1C-01"). Setting up a blank needs qr.manage, registering a spool material.instance.create, and weighing or marking a spool empty material.instance.edit.
An available action that needs a choice carries a param, with its options and the default to pick. Send the pick to execute as params:
| param.kind | Options | Send |
|---|---|---|
slot |
The printer's slots: amsIndex, slotIndex, ref, group ("AMS 1", "External"), assigned, loaded. The default follows the store's slotStrategy: for first_empty (the default) the first slot with nothing assigned and no spool, or for a spool, first a slot the printer sees a spool in with nothing assigned; for external the external spool holder; null for prompt, or when no slot fits. |
amsIndex, slotIndex |
printer_slot |
Every printer with its slots. | printerId, amsIndex, slotIndex |
choice |
key is jobId (the next jobs in the queue), macroId (macros for the printer's model) or enrollmentId (maintenance tasks, most overdue first), with options of value, label and an optional hint. The default is the first option. |
{ [key]: value } |
variant |
Pick from the store's materials (GET /v1/materials/full). With material.instance.view, spools lists the store's spools with filament left (id, variantId, remainingGrams, code, location), so a spool of the picked variant can be loaded instead. |
variantId, and instanceId for a spool |
grams |
A weight: default is what the entry opens with (the store's defaultSpoolGrams, 1000 unless changed, to register a spool; the spool's weight to update it) and min the least it takes (1 to register, 0 to update). At most 50,000. |
grams |
Request Body
The scanned QR text, one or two payloads
Example Request
{
"payloads": [
"string"
],
"refresh": true,
"target": {
"entityType": "printer",
"entityId": "string"
}
}
Examples
A printer waiting for confirmation
{
"payloads": [
"{\"v\":1,\"t\":\"printer\",\"id\":\"k3q9x2m8v7c1n4b6z0w5y2ta\",\"s\":\"cm1r8s2t4u6v8w0x2y4z6a8b\",\"d\":{\"printerId\":\"p7h2j4k6m8n0q2r4s6t8v0wa\"}}"
]
}
{
"mode": "single",
"tags": [
{
"id": "k3q9x2m8v7c1n4b6z0w5y2ta",
"status": "active",
"entityType": "printer",
"entityId": "p7h2j4k6m8n0q2r4s6t8v0wa",
"label": "X1C-01",
"printer": {
"id": "p7h2j4k6m8n0q2r4s6t8v0wa",
"name": "X1C-01",
"provider": "Bambu",
"state": "Waiting",
"statusLabel": "Waiting for confirmation",
"printing": false,
"paused": false,
"progress": null,
"jobName": null
}
}
],
"actions": [
{
"id": "printer.confirmReady",
"label": "Confirm ready",
"kind": "builtin",
"runs": "server",
"permission": "printer.ready",
"default": true,
"destructive": false,
"confirm": false,
"available": true
},
{
"id": "printer.pause",
"label": "Pause",
"kind": "builtin",
"runs": "server",
"permission": "printer.control",
"default": false,
"destructive": false,
"confirm": false,
"available": false,
"reasonCode": "not_printing",
"reason": "Nothing is printing"
}
]
}
A filament variant and a printer
{
"mode": "pair",
"tags": [
{
"id": "u8qaisrqkwr6ib1p1jfjeiov",
"status": "active",
"entityType": "printer",
"entityId": "p7h2j4k6m8n0q2r4s6t8v0wa",
"label": "X1C-01",
"printer": {
"id": "p7h2j4k6m8n0q2r4s6t8v0wa",
"name": "X1C-01",
"provider": "Bambu",
"state": "Waiting",
"statusLabel": "Waiting for confirmation",
"printing": false,
"paused": false,
"progress": null,
"jobName": null
}
},
{
"id": "nvioxp9s1d7o3van7wqhkmtp",
"status": "active",
"entityType": "material_variant",
"entityId": "v2n4m6b8c0x2z4a6s8d0f2gh",
"label": "PLA Matte · Ivory White",
"printer": null,
"variant": {
"id": "v2n4m6b8c0x2z4a6s8d0f2gh",
"name": "Ivory White",
"materialId": "m3b5n7m9q1w3e5r7t9y1u3io",
"materialName": "PLA Matte",
"brand": "Bambu Lab",
"type": "PLA",
"color": "#FFFFFFFF"
}
}
],
"actions": [
{
"id": "pair.assignVariantToPrinter",
"label": "Assign to a slot",
"kind": "builtin",
"runs": "server",
"permission": "printer.edit",
"default": true,
"destructive": false,
"confirm": false,
"available": true,
"param": {
"kind": "slot",
"options": [
{
"amsIndex": 0,
"slotIndex": 0,
"ref": "A1/1",
"group": "AMS 1",
"assigned": {
"name": "PETG HF Red",
"brand": "Bambu Lab",
"color": "#EB3A3AFF"
},
"loaded": true
},
{
"amsIndex": 0,
"slotIndex": 1,
"ref": "A1/2",
"group": "AMS 1",
"assigned": null,
"loaded": false
},
{
"amsIndex": 0,
"slotIndex": 2,
"ref": "A1/3",
"group": "AMS 1",
"assigned": {
"name": "PLA Basic Black",
"brand": "Bambu Lab",
"color": "#000000FF"
},
"loaded": true
},
{
"amsIndex": 0,
"slotIndex": 3,
"ref": "A1/4",
"group": "AMS 1",
"assigned": null,
"loaded": true
},
{
"amsIndex": -1,
"slotIndex": 0,
"ref": "S1",
"group": "External",
"assigned": null,
"loaded": false
}
],
"default": {
"amsIndex": 0,
"slotIndex": 1
}
}
}
]
}
Two slots, one with filament
{
"mode": "pair",
"tags": [
{
"id": "uyk6qzl3cah7b8nccab3yigm",
"status": "active",
"entityType": "printer_slot",
"entityId": "p7h2j4k6m8n0q2r4s6t8v0wa",
"label": "A1/1 · X1C-01",
"printer": {
"id": "p7h2j4k6m8n0q2r4s6t8v0wa",
"name": "X1C-01",
"provider": "Bambu",
"state": "Waiting",
"statusLabel": "Waiting for confirmation",
"printing": false,
"paused": false,
"progress": null,
"jobName": null
},
"slot": {
"amsIndex": 0,
"slotIndex": 0,
"ref": "A1/1",
"assigned": {
"name": "PETG HF Red",
"brand": "Bambu Lab",
"color": "#EB3A3AFF"
},
"loaded": true,
"exists": true
}
},
{
"id": "u94wm8gdm5eznvkrigjns4o1",
"status": "active",
"entityType": "printer_slot",
"entityId": "p7h2j4k6m8n0q2r4s6t8v0wa",
"label": "A1/2 · X1C-01",
"printer": {
"id": "p7h2j4k6m8n0q2r4s6t8v0wa",
"name": "X1C-01",
"provider": "Bambu",
"state": "Waiting",
"statusLabel": "Waiting for confirmation",
"printing": false,
"paused": false,
"progress": null,
"jobName": null
},
"slot": {
"amsIndex": 0,
"slotIndex": 1,
"ref": "A1/2",
"assigned": null,
"loaded": false,
"exists": true
}
}
],
"actions": [
{
"id": "pair.moveSlot",
"label": "Move PETG HF Red to A1/2",
"kind": "builtin",
"runs": "server",
"permission": "printer.edit",
"default": true,
"destructive": false,
"confirm": false,
"available": true
}
]
}
A printer and one of its own slots
{
"mode": "invalid_pair",
"message": "That slot is on this printer. Scan the slot on its own to see what it can do.",
"tags": [
"…both tags, as above…"
],
"actions": []
}
A revoked tag
{
"payloads": [
"{\"v\":1,\"t\":\"printer\",\"id\":\"k3q9x2m8v7c1n4b6z0w5y2ta\",\"s\":\"cm1r8s2t4u6v8w0x2y4z6a8b\",\"d\":{\"printerId\":\"p7h2j4k6m8n0q2r4s6t8v0wa\"}}"
]
}
{
"mode": "revoked",
"message": "This tag was revoked. Print a new tag for it from Printago on the web.",
"tags": [
{
"id": "k3q9x2m8v7c1n4b6z0w5y2ta",
"status": "revoked",
"entityType": "printer",
"entityId": "p7h2j4k6m8n0q2r4s6t8v0wa",
"label": "X1C-01",
"printer": null
}
],
"actions": []
}
Response Schema
Example Response
{
"mode": "revoked",
"tags": [
{
"id": "string",
"status": "blank",
"entityType": "blank",
"entityId": "string",
"label": "string",
"printer": {
"id": "string",
"name": "string",
"provider": "string",
"state": "Offline",
"statusLabel": "string",
"statusUnknown": true,
"printing": true,
"paused": true,
"progress": 1,
"jobName": "string"
},
"slot": {
"amsIndex": 1,
"slotIndex": 1,
"ref": "string",
"exists": true,
"assigned": {
"name": "string",
"brand": "string",
"color": "string",
"instanceId": "string",
"remainingGrams": 1
},
"loaded": true
},
"variant": {
"id": "string",
"name": "string",
"materialId": "string",
"materialName": "string",
"brand": "string",
"type": "string",
"color": "string"
},
"spool": {
"id": "string",
"variant": {
"id": "string",
"name": "string",
"materialId": "string",
"materialName": "string",
"brand": "string",
"type": "string",
"color": "string"
},
"remainingGrams": 1,
"nominalGrams": 1,
"labelCode": "string",
"empty": true,
"location": {
"printerId": "string",
"printerName": "string",
"amsIndex": 1,
"slotIndex": 1,
"ref": "string"
}
},
"blank": {
"code": "string"
}
}
],
"actions": [
{
"id": "printer.confirmReady",
"label": "string",
"kind": "webhook",
"runs": "server",
"permission": "organization.owner",
"default": true,
"destructive": true,
"confirm": true,
"confirmMessage": "string",
"available": true,
"reasonCode": "printing",
"reason": "string",
"detail": "string",
"param": {
"kind": "slot",
"options": [
{
"amsIndex": 1,
"slotIndex": 1,
"ref": "string",
"group": "string",
"assigned": {
"name": "string",
"brand": "string",
"color": "string",
"instanceId": "string",
"remainingGrams": 1
},
"loaded": true
}
],
"default": {
"amsIndex": 1,
"slotIndex": 1
}
}
}
],
"message": "string"
}