Work Queues API
Add items, scan barcodes, claim and move work, run stage buttons and read queue reports.
Work Queues track items through a series of stages: boxes arriving at a warehouse, orders being packed, tickets being reviewed. This API puts items into a queue, scans barcodes, lets stations claim work, moves items between stages and reports what is where. Queues themselves (their stages and rules) are normally set up in the app.
If the Work Queues app is switched off in Settings › Apps, these routes answer 404 NOT_FOUND.
Endpoints
Queue ids are UUIDs. Permissions are checked against the queue (its key is the resource), so a role can be limited to one queue.
| Method | Path | Permission | What it does |
|---|---|---|---|
GET |
/orgs/{orgId}/queues |
queues:read |
List queues you can see, with counts |
POST |
/orgs/{orgId}/queues |
queues:create |
Create a queue |
GET |
/orgs/{orgId}/queues/{queueId} |
queues:read |
Get a queue |
PUT |
/orgs/{orgId}/queues/{queueId} |
queues:update |
Replace a queue's definition |
DELETE |
/orgs/{orgId}/queues/{queueId} |
queues:delete |
Delete a queue |
GET |
/orgs/{orgId}/queues/{queueId}/items |
queues:read |
List and filter items |
POST |
/orgs/{orgId}/queues/{queueId}/items |
queues:enqueue |
Add one or many items |
GET |
/orgs/{orgId}/queues/{queueId}/items/{itemId} |
queues:read |
Get an item |
PATCH |
/orgs/{orgId}/queues/{queueId}/items/{itemId} |
queues:transition |
Edit an item's own fields |
DELETE |
/orgs/{orgId}/queues/{queueId}/items/{itemId} |
queues:delete |
Delete an item |
POST |
/orgs/{orgId}/queues/{queueId}/items/{itemId}/move |
queues:transition |
Move an item to a stage |
POST |
/orgs/{orgId}/queues/{queueId}/scan |
queues:transition |
Scan a key: advance or create |
POST |
/orgs/{orgId}/queues/{queueId}/claim |
queues:transition |
Take waiting items |
GET |
/orgs/{orgId}/queues/{queueId}/board |
queues:read |
Every stage with its first items |
GET |
/orgs/{orgId}/queues/{queueId}/stages/{stageKey}/items |
queues:read |
One stage's items |
GET |
/orgs/{orgId}/queues/{queueId}/summary |
queues:read |
Counts and verification report |
GET |
/orgs/{orgId}/queues/{queueId}/batches |
queues:read |
Batch labels in use |
GET |
/orgs/{orgId}/queues/{queueId}/events |
queues:read |
Movement history |
POST |
/orgs/{orgId}/queues/{queueId}/stages/{stageKey}/action |
queues:transition and functions:invoke |
Run a stage button on items |
enqueue and transition are separate on purpose: a scanning station can add work without being able to move it through the pipeline, and a supervisor can move work without being able to invent it.
The queue object
{
"id": "0190f0ff-…",
"key": "inbound-boxes",
"name": "Inbound boxes",
"object_type": "",
"enabled": true,
"stages": [
{ "key": "expected", "label": "Expected", "kind": "start", "position": 0, "from": [], "wip_limit": 0, "sla_seconds": 0, "buttons": [] },
{ "key": "received", "label": "Received", "kind": "normal", "position": 1, "from": ["expected"], "wip_limit": 0, "sla_seconds": 3600, "buttons": [] },
{ "key": "shelved", "label": "Shelved", "kind": "terminal", "position": 2, "from": [], "wip_limit": 0, "sla_seconds": 0, "buttons": [] }
],
"intake": { "enabled": false, "filter": [], "stage": "", "key_field": "", "close_on_unmatch": false },
"settings": { "on_unknown_scan": "reject", "lease_seconds": 300, "allow_duplicates": false },
"summary": { "total": 120, "open": 80, "closed": 40, "overdue": 3, "unexpected": 0, "by_stage": { "expected": 20 } }
}
Stage kind is start, normal or terminal. A terminal stage closes the item. from limits which stages may move into this one (empty means any). wip_limit caps how many items may sit in the stage (0 means no cap). sla_seconds marks items overdue after that long.
Creating a queue
POST and PUT take the same body: key (lowercase letters, digits, - or _, up to 60 characters; made from the name when omitted), name, object_type (optional record type to link items to), enabled, stages (up to 40, each with key, label, kind, from, wip_limit, sla_seconds, record_patch, on_enter, buttons), intake and settings. A PUT should echo the stage keys back so items keep their place. The key cannot be changed afterwards. Up to 200 queues per workspace (past that: 400 VALIDATION_ERROR). Errors: 409 KEY_TAKEN, 400 VALIDATION_ERROR.
The item object
{
"id": "0190f100-…",
"key": "BOX-000123",
"stage": "received",
"status": "open",
"priority": 0,
"source": "scan",
"batch": "2026-10-04-truck-7",
"record_id": "0190f0aa-…",
"payload": { "weight_kg": 12.5 },
"claimed_by": "0190f0a0-…",
"claim_expires_at": "2026-10-04T09:35:00Z",
"assignee_id": "",
"attempts": 0,
"due_at": "2026-10-04T10:30:00Z",
"overdue": false,
"entered_stage_at": "2026-10-04T09:30:00Z",
"created_at": "2026-10-04T08:00:00Z"
}
status is open, done, failed or cancelled. source is manual, scan, import, intake or function. Items can carry up to 16 KB of payload.
POST /orgs/{orgId}/queues/{queueId}/items
Add items. The key is the item's identity in the queue, so adding a key that is already open is a harmless no-op (unless the queue is set to allow duplicates): a double scan or a retried request does not create duplicates.
| Field | Type | Required | Notes |
|---|---|---|---|
items |
array | One of items or keys |
1 to 500 items. Each has key, and optionally stage, batch, priority, record_id, assignee_id, payload |
keys |
array of strings | Shorthand for a pasted manifest. Ignored when items is present |
|
stage |
string | No | Default stage for items that do not name one. The start stage if omitted |
batch |
string | No | A label to group a load, for example a delivery |
source |
string | No | manual (default), scan, import, intake or function. Items created with source scan are counted as unexpected in the report, so use import for a manifest |
note |
string | No | Recorded on each item's history |
curl -s -X POST https://axisiq.co/api/v1/orgs/$ORG/queues/$Q/items \
-H "Authorization: Bearer $AXIS_KEY" -H "Content-Type: application/json" \
-d '{"batch": "truck-7", "keys": ["BOX-000123", "BOX-000124", "BOX-000125"]}'
Response 201
{ "data": { "created": 3, "duplicates": 0, "items": [ { "id": "…", "key": "BOX-000123", "stage": "expected", "status": "open" } ] } }
Stage limits (work in progress caps) do not apply when adding items, only when moving them.
POST /orgs/{orgId}/queues/{queueId}/scan
Look an item up by its key and move it on, as a barcode scanner does. It always answers 200; the verdict tells you what happened.
| Field | Type | Required | Notes |
|---|---|---|---|
key |
string | Yes | The barcode or item key |
stage |
string | No | Where a new item should be created (when to_stage is not given). The start stage if omitted |
to_stage |
string | No | Where a matching item should go (or where a new one is created). The next stage if omitted for an existing item |
batch |
string | No | |
note |
string | No | |
payload |
object | No |
verdict |
Meaning |
|---|---|
moved |
The item existed and was advanced |
created |
The key was new and the queue accepts unknown keys, so an item was created (this also needs queues:enqueue, otherwise 403 FORBIDDEN) |
duplicate |
The item is already at the target stage (a second beep) |
unknown |
The key is not in this queue and the queue rejects unknown keys |
blocked |
A stage rule refused the move (not an allowed source stage, or the stage is full) |
{ "data": { "verdict": "moved", "message": "moved to received", "item": { "id": "…", "key": "BOX-000123", "stage": "received" } } }
POST /orgs/{orgId}/queues/{queueId}/items/{itemId}/move
| Field | Type | Required | Notes |
|---|---|---|---|
to_stage |
string | Yes | Stage key |
status |
string | No | open, done, failed or cancelled. Default: done when entering a terminal stage, otherwise open |
note |
string | No |
Response 200 with the item. A move can be refused with 400 VALIDATION_ERROR when the target stage does not accept items from the current stage, or when it is at its limit. If the stage updates a linked record, that update is made first and as you, so a failure there stops the move.
POST /orgs/{orgId}/queues/{queueId}/claim
Take up to count waiting items from a stage for yourself. Used by pickers who each pull their own work. Two callers never receive the same item.
| Field | Type | Notes |
|---|---|---|
stage |
string | Optional. Stage to pull from |
count |
number | Optional. Default 1, at most 50 (more is 400 VALIDATION_ERROR) |
Response 200: { "items": [ … ], "claimed": 2 }. A claim lasts 5 minutes by default (set per queue) and then returns to the pool if not finished.
PATCH /orgs/{orgId}/queues/{queueId}/items/{itemId}
Change an item without moving it: priority, batch, assignee_id (empty string clears), due_at, payload, release (true gives up your claim), note. Only the stage cannot be changed here; use move.
GET /orgs/{orgId}/queues/{queueId}/items
| Parameter | Notes |
|---|---|
stage, status, batch, key |
Filters |
q |
Search text |
overdue |
true for overdue items only |
assignee_id, record_id |
Filter by assignee or linked record |
limit |
Default 50, maximum 200 |
offset |
Start position |
Returns { "items": [ … ], "total": 120, "next_offset": 50 }. next_offset is null on the last page. The "missing items" report is ?stage=expected&status=open.
GET /orgs/{orgId}/queues/{queueId}/summary
Optional batch. Returns the verification report:
{
"data": {
"queue": { "id": "…", "key": "inbound-boxes", "name": "Inbound boxes" },
"summary": { "total": 120, "open": 80, "closed": 40, "overdue": 3, "unexpected": 2, "by_stage": { "expected": 20, "received": 60, "shelved": 40 } }
}
}
With a batch loaded as a manifest, items still sitting in the starting stage are the ones missing, and items created by a scan that nobody expected are counted as unexpected.
Other reads
GET …/board?limit=&batch=returns{ "queue": {…}, "stages": [ … ] }, each stage with its firstitems,totalandnext_offset.GET …/stages/{stageKey}/itemsaccepts the same filters as the item list.GET …/batcheslists batch labels.GET …/events?item_id=&limit=returns the movement history (default 100, maximum 500).
POST /orgs/{orgId}/queues/{queueId}/stages/{stageKey}/action
Runs a stage's button, a function, over up to 25 items, as you. Needs functions:invoke as well.
{ "button_id": "…", "item_ids": ["0190f100-…", "0190f101-…"] }
Returns one result per item: [{ "item_id": "…", "ok": true }, { "item_id": "…", "ok": false, "error": "…" }].
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR |
Bad body, bad id, 1 to 500 items rule, payload over 16 KB, a stage rule refused the move |
| 403 | FORBIDDEN |
Missing the queue permission for this queue |
| 404 | NOT_FOUND |
No such queue, item, stage or button |
| 409 | KEY_TAKEN |
A queue with that key already exists |
Related
- Functions SDK:
axis.queue.*does the same from code, addressing queues by key - In the product: Work Queues