API: devices and inventory
General contract in How the API works.
POST /devices and DELETE /devices/:id require the reserved device_provisioning resource: a key
gets 403. Everything else — read, edit, move, activate/deactivate and inventory — is available.
Querying
GET /devices
GET /devices/:id
List query: operationId, businessLine, isActive, type (spring, locker, liquid, coffee,
return), search (name, serial or city), page, pageSize (max 1000).
A device response (trimmed):
{
"id": "665f…d1",
"serial": "ICS595DDE99B",
"name": "Vending Corferias 1",
"businessLine": "vending",
"currency": "COP",
"accountId": "665f…c6",
"operationId": "665f…c7",
"specs": {
"type": "spring",
"brand": "INSSA SNACKS",
"model": "IKA 5 COMBI",
"paymentMethods": { "cashless": true, "cash": false, "card": true }
},
"location": { "country": "Colombia", "city": "Bogotá", "address": "Corferias", "coordinates": null },
"controller": { "serial": "ICS595DDE99B", "status": "online" },
"planogramKind": "physical",
"isActive": true,
"lastTransmission": "2026-08-11T13:58:22.000Z"
}
Editing and moving
PATCH /devices/:id { "name": "…", "location": { … }, "specs": { … } }
PATCH /devices/:id/scope { "operationId": "665f…c8" }
PATCH /devices/:id/deactivate
PATCH /devices/:id/activate
PATCH /devices/:id accepts name, location, specs and peripherals; brand and peripherals can
only be changed by a global operator (403 for everyone else).
PATCH /devices/:id/scope moves the device to another operation. A key can only move it within its
account; moving across accounts needs client or global level.
Activate and deactivate answer 204.
Inventory (planogram)
PUT /devices/:id/planogram
PATCH /devices/:id/slots/:selection
POST /devices/:id/slots/:selection/load
DELETE /devices/:id/slots/:selection
They require device_inventory:update, which the key has.
Replacing the whole planogram
PUT /devices/:id/planogram — send the final structure: rows and slots. It replaces whatever was
there, so anything you omit is deleted.
{
"kind": "physical",
"rows": [{ "rowId": "R1", "name": "Bandeja 1", "capacity": 12 }],
"slots": [
{
"selection": "11",
"rowId": "R1",
"productId": "665f…b1",
"maxQty": 20,
"minQty": 3,
"currentQty": 12,
"price": 3000
}
]
}
Rules: every slot references a declared row, the selection is unique (uppercase and digits, max 8),
and return modules take no planogram (400 devices.planogram_not_applicable).
Adjusting one slot
PATCH /devices/:id/slots/:selection — optional fields, null to clear:
| Field | Type | Notes |
|---|---|---|
productId | string | null | null empties the slot |
maxQty / minQty / currentQty | integer | null | ≥ 0 |
price | integer | null | minor units. In cashless the device reports the charge |
Restocking
POST /devices/:id/slots/:selection/load — { "productId": "665f…b1", "qty": 20, "price": 3000 }.
This is the natural endpoint for the restocking process: sets product and quantity in one go.
DELETE /devices/:id/slots/:selection empties the slot.
They all return 200 with the updated device (or 204 on the DELETE).
Own errors: 404 devices.not_found, 404 devices.slot_not_found,
409 devices.slot_conflict, 400 devices.product_mismatch,
400 devices.invalid_planogram_shape, 403 devices.not_visible_to_scope.