API reference
All paths below are relative to /api/v1. Download OpenAPI 3.1 JSON →
Account
GET/meCurrent user, inventory, membership, and quota
inventory:read
Success: 200 · application/json
Inventory
GET/homeHome dashboard and hierarchy
inventory:read
Success: 200 · application/json
GET/kindsAvailable node kinds
inventory:read
Success: 200 · application/json
GET/nodesList and filter inventory
inventory:read
Stable ascending UUID order. Follow nextCursor until null. Pages are not a point-in-time snapshot; records created during a scan may require another pass.
Query parameters
{
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 50
},
"cursor": {
"type": "string",
"format": "uuid"
},
"kind": {
"enum": [
"location",
"box",
"bag",
"item"
]
},
"parentId": {
"type": "string",
"description": "Parent UUID or root for top-level records."
},
"unnamed": {
"type": "boolean"
},
"hasPhoto": {
"type": "boolean"
},
"archived": {
"type": "boolean"
}
}Success: 200 · application/json
POST/nodesCreate a record
inventory:write
Items and places need a name. Boxes and bags may be unnamed. For unnamed photo capture, use the capture endpoint.
JSON request schema
{
"type": "object",
"properties": {
"kind": {
"enum": [
"location",
"box",
"bag",
"item"
]
},
"name": {
"type": "string",
"maxLength": 500
},
"parentId": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"quantity": {
"type": [
"number",
"string",
"null"
]
}
},
"required": [
"kind"
]
}Success: 201 · application/json
GET/nodes/{slug}Record detail and current revision
inventory:read
Use a record UUID as slug. Legacy printed slugs remain resolvable. Includes parent, breadcrumb, attachments, tags, label status, readOnly, and moveCandidates. intent=preview-subtree-delete returns a deletion preview instead.
Query parameters
{
"intent": {
"enum": [
"preview-subtree-delete"
]
}
}Success: 200 · application/json
PATCH/nodes/{slug}Update record fields
inventory:write
Omitted fields stay unchanged; nullable fields can be cleared with null. PAT and OAuth integrations must send If-Match with the last node revision to reject concurrent edits with 409. Dates use YYYY-MM-DD; amounts use integer cents.
JSON request schema
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"description": {
"type": [
"string",
"null"
]
},
"quantity": {
"type": [
"number",
"string",
"null"
],
"description": "Non-negative quantity; returned as decimal text."
},
"unit": {
"type": [
"string",
"null"
]
},
"status": {
"type": [
"string",
"null"
]
},
"brand": {
"type": [
"string",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"serialNumber": {
"type": [
"string",
"null"
]
},
"condition": {
"type": [
"string",
"null"
]
},
"currency": {
"type": [
"string",
"null"
]
},
"purchasePriceCents": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"replacementCostCents": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"purchasedAt": {
"type": [
"string",
"null"
],
"format": "date"
},
"warrantyExpiresAt": {
"type": [
"string",
"null"
],
"format": "date"
}
}
}Success: 200 · application/json
DELETE/nodes/{slug}Delete one record
inventory:delete
Irreversible: removes the record and its attachments; direct children move to its parent. This does not delete the whole subtree.
Success: 200 · application/json
POST/nodes/{slug}Confirm subtree deletion or use legacy move
inventory:delete
Preview deletion with GET ?intent=preview-subtree-delete and submit its fingerprint and short code. Changed subtrees return 409. Integrations should use /scan/move for ordinary moves with inventory:write.
JSON request schema
{
"oneOf": [
{
"type": "object",
"properties": {
"intent": {
"const": "delete-subtree"
},
"confirmation": {
"type": "object",
"properties": {
"fingerprint": {
"type": "string"
},
"shortCode": {
"type": "string"
}
},
"required": [
"fingerprint",
"shortCode"
]
}
},
"required": [
"intent",
"confirmation"
]
},
{
"type": "object",
"properties": {
"intent": {
"const": "move"
},
"parentId": {
"type": [
"string",
"null"
]
}
},
"required": [
"intent",
"parentId"
]
}
]
}Success: 200 · application/json
GET/nodes/{slug}/childrenDirect contents of a container
inventory:read
Success: 200 · application/json
GET/searchSearch inventory
inventory:read
Fuzzy name, code, tag, and breadcrumb search. Returns q, results, and exactMatch.
Query parameters
{
"q": {
"type": "string",
"minLength": 2
}
}Success: 200 · application/json
GET/cleanupRecords needing attention
inventory:read
Success: 200 · application/json
History & export
GET/historyPaginated audit history
inventory:read
Newest first. Events survive record deletion. Integration events identify the account and token or OAuth client.
Query parameters
{
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 50
},
"cursor": {
"type": "string",
"format": "uuid"
},
"nodeId": {
"type": "string",
"format": "uuid"
}
}Success: 200 · application/json
GET/nodes/{slug}/exportExport a record and its subtree
inventory:read
Includes fields, breadcrumbs, tags, and attachment metadata. JSON is the default; format=csv downloads CSV. Images require separate authenticated downloads. Export remains available when storage is read-only.
Query parameters
{
"format": {
"enum": [
"json",
"csv"
],
"default": "json"
}
}Success: 200 · application/json
Photos & receipts
GET/nodes/{slug}/attachmentsList attachment metadata
inventory:read
Success: 200 · application/json
POST/nodes/{slug}/attachmentsUpload a photo or receipt
photos:write
Multipart upload, maximum 15 MB per file. Photos accept JPEG, PNG, WebP, HEIC, or HEIF. Receipts also accept PDF. Images are normalized and compressed by the server. Storage exhaustion returns 402.
Multipart request schema
{
"type": "object",
"properties": {
"kind": {
"enum": [
"photo",
"receipt"
]
},
"file": {
"type": "string",
"format": "binary"
},
"caption": {
"type": "string"
}
},
"required": [
"kind",
"file"
]
}Success: 201 · application/json
GET/attachments/{id}Attachment metadata and download path
inventory:read
Success: 200 · application/json
GET/attachments/{id}/contentDownload a private image or PDF
photos:read
Returns original stored bytes with Content-Type image/jpeg or application/pdf. Foreign and unknown IDs both return 404. URLs are private; send the bearer header on every download.
Success: 200 · image/jpeg
DELETE/attachments/{id}Delete an attachment
photos:delete
Irreversible. Repoints or clears the cover photo when necessary.
Success: 200 · application/json
Tags & classification
GET/tagsList inventory tags
inventory:read
Success: 200 · application/json
GET/nodes/{slug}/tagsList a record’s tags
inventory:read
Success: 200 · application/json
POST/nodes/{slug}/tagsAdd a category or tag
inventory:write
Creates the tag if needed; attaching an existing tag is idempotent. Other tags remain unchanged.
JSON request schema
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
}
},
"required": [
"name"
]
}Success: 201 · application/json
DELETE/nodes/{slug}/tags/{tagId}Remove a tag from a record
inventory:write
Success: 200 · application/json
Capture & scan
POST/scan/resolveResolve a printed code or URL
inventory:read
Unrecognized scans return HTTP 200 with ok:false and reason invalid or not-found.
JSON request schema
{
"type": "object",
"properties": {
"code": {
"type": "string"
},
"manual": {
"type": "boolean"
}
},
"required": [
"code"
]
}Success: 200 · application/json
POST/scan/moveMove a record
inventory:write
destId is a container UUID, or null for the top level. Invalid containment and cycles return 409.
JSON request schema
{
"type": "object",
"properties": {
"itemId": {
"type": "string",
"format": "uuid"
},
"destId": {
"type": [
"string",
"null"
],
"format": "uuid"
}
},
"required": [
"itemId",
"destId"
]
}Success: 200 · application/json
GET/capture/contextPlaces, recent destinations, and capture context
inventory:read
Query parameters
{
"bin": {
"type": "string"
}
}Success: 200 · application/json
POST/captureNative capture workflow
inventory:write + photos:write
Existing mobile multipart workflow. Integrations use PATCH with If-Match instead of rename-item; finish with print=on additionally requires labels:print. Fields depend on intent; see the mobile capture contract in the repository. For scripts, prefer creating a record and uploading its attachment separately.
Multipart request schema
{
"type": "object",
"properties": {
"intent": {
"enum": [
"mint-and-capture",
"capture-item",
"resolve-destination",
"mint-place",
"move-captured",
"rename-item",
"finish"
]
}
},
"required": [
"intent"
]
}Success: 200 · application/json
GET/products/lookupLook up a retail barcode
inventory:read
Returns found and normalized gtin, with optional name, brand, model, and source. Provider unavailable returns 503.
Query parameters
{
"code": {
"type": "string"
}
}Success: 200 · application/json
Contents checks
GET/nodes/{slug}/checksActive and recent box checks
inventory:read
Success: 200 · application/json
POST/nodes/{slug}/checksStart or update a contents check
inventory:write
start needs no checkId. Other operations require checkId and current revision. mark/remove need entryId; mark also needs status; unexpected needs name; scan needs code. Stale revisions return 409.
JSON request schema
{
"type": "object",
"properties": {
"intent": {
"enum": [
"start",
"mark",
"remove",
"unexpected",
"scan",
"finish",
"cancel"
]
},
"checkId": {
"type": "string",
"format": "uuid"
},
"revision": {
"type": "integer",
"minimum": 0
},
"entryId": {
"type": "string",
"format": "uuid"
},
"status": {
"enum": [
"present",
"missing",
"unchecked"
]
},
"name": {
"type": "string",
"maxLength": 200
},
"code": {
"type": "string",
"maxLength": 2048
}
},
"required": [
"intent"
]
}Success: 200 · application/json
Labels & printing
GET/nodes/{slug}/label.payloadDownload M110 binary print data
labels:read
Returns printer bytes with X-Payload-Sha256. The mover template is not supported for BLE and returns 422.
Query parameters
{
"template": {
"enum": [
"standard",
"code"
],
"default": "standard"
}
}Success: 200 · application/octet-stream
POST/nodes/{slug}/print-jobsQueue physical labels
labels:print
Queues physical output on the configured bridge. Obtain user intent before printing. room and flags apply to mover labels.
JSON request schema
{
"type": "object",
"properties": {
"template": {
"enum": [
"standard",
"mover",
"code"
]
},
"copies": {
"type": "integer",
"minimum": 1,
"maximum": 50
},
"room": {
"type": "string"
},
"flags": {
"type": "array",
"items": {
"enum": [
"Fragile",
"Open first"
]
}
}
}
}Success: 200 · application/json
POST/nodes/{slug}/printsRecord a successful direct print
labels:print
Call only after a direct print succeeds. This records completion and does not queue another print.
JSON request schema
{
"type": "object",
"properties": {
"template": {
"enum": [
"standard",
"code"
]
}
},
"required": [
"template"
]
}Success: 200 · application/json
AI enrichment
GET/enrichment/batchesRecent enrichment batches
inventory:read
The most recent 100 batches, without individual changes.
Success: 200 · application/json
POST/enrichment/batchesPreview a batch of suggestions
inventory:write
Saves before/after proposals for 1–50 records. Does not change inventory. Supported fields: name, description, brand, model, condition, status. Use tags for categories.
JSON request schema
{
"$ref": "#/components/schemas/EnrichmentInput"
}Success: 201 · application/json
GET/enrichment/batches/{id}Read a saved batch
inventory:read
Success: 200 · application/json
POST/enrichment/batches/{id}/applyApply a reviewed batch
inventory:write
Atomic and single-use. If any record changed or disappeared after preview, returns 409 and changes nothing.
Success: 200 · application/json
POST/enrichment/batches/{id}/undoUndo an applied batch
inventory:write
Restores the fields changed by this batch. If any record was edited after application, returns 409 and changes nothing.
Success: 200 · application/json
Errors
| Status | What to do |
|---|---|
| 400 | Fix the input; read the error code and message. |
| 401 | Sign in again, refresh OAuth access, or replace the expired/revoked token. |
| 402 | The inventory needs storage credits for this operation. |
| 403 | Use a token with the required scopes and a membership that permits them. |
| 404 | The record is missing or outside the authorized inventory. |
| 405 | Use the documented HTTP method. |
| 409 | Read current state and make a new preview or use the new revision. |
| 413 | Reduce the upload size (maximum 15 MB). |
| 422 | Choose a supported label template. |
| 428 | Read the record and send its revision in If-Match. |
| 429 | Wait for Retry-After seconds; reduce request frequency. |
| 500 / 503 | Retry later. Check whether a write completed before repeating it. |