Skip to main content
A device is a client (a sensor, a printer, a kiosk, a desktop) that holds a connection to the platform with a device credential, declares the commands it accepts, and can send events. Each command becomes a tool the agent can call while the device is connected; each event is a device trigger the agent handles in code on the server. Devices exist so an agent can act on the physical world without an intermediary service between the hardware and the model.

How a device connects

A device connects over one of two transports: Socket.IO over WebSocket, the default for the Node client, or MQTT, used by the Node, Python, and MicroPython clients. The MQTT clients default to MQTT over WebSocket with TLS on port 443 (wss://mqtt.heylua.ai/mqtt), which passes most firewalls; the clients also accept a raw mqtts:// URL. At connect the client presents a device credential: a key of the form api_<uuid>.<secret> issued for one agent, one device name, and a set of operations (commands, triggers, assets.upload). The platform re-checks the credential while the device stays connected, so revoking it takes the device offline. With the connection the device declares its commands: a name, a description, and an optional JSON Schema for the input. The platform turns each into a tool named device__<device>__<command> (hyphens in the device name become underscores), described as [Device: <name>] <description>. If the device is offline, this will return an error., and adds device__<device>__is_online, which answers { status: 'online' } or, for a device that isn’t connected, its stored offline, registered, or disabled. The model calls them like any other tool; the platform forwards the call, waits up to the command’s timeout (30 seconds by default), and returns the device’s reply. When a device that declared its own commands (a self-describing device) disconnects, its command tools are gone on the agent’s next turn; a device declared with defineDevice keeps its tools while offline, and calls to it return DEVICE_OFFLINE. A self-describing device’s stored command list also expires 24 hours after it connected, and heartbeats don’t extend it, so a device connected for longer than a day loses its command tools until it reconnects. Either way the prompt tells the model to check is_online before acting on a device. Events travel the other way. The device sends a trigger with a name and a payload, and the platform runs the matching handler on the server: the execute of a defineDeviceTrigger you pushed, or of a trigger declared inside defineDevice. The handler runs with the platform APIs in scope (Channels, User, Data, Agents), so a “temperature exceeded” event can become a message to an operator or a stored alert; it receives no environment variables, so env() is empty there. Its second argument carries only the device and trigger names; the agent field in its type is not populated at runtime, so don’t call it. Events are delivered at least once from a durable queue: a handler that throws or times out runs again, up to 3 attempts 60 seconds apart with the same triggerId, so handlers must be idempotent, and each attempt has up to 10 minutes, the platform’s default cap for event handlers. An event with no pushed handler is acknowledged and skipped.
src/devices/label-printer.device.ts
defineDevice and defineDeviceTrigger are the agent-side declarations, registered on LuaAgent.devices and deviceTriggers. A self-describing device needs no defineDevice to get tools, but the declaration is where command timeouts and retries, a group for fan-out (device__<group>__<command>__all sends to every registered device in the group, online or not; an offline member fails with a 404 and counts in failed; membership is the group each device reports when it connects), and trigger handlers live. A command is not retried unless you set retry, and then only after a timeout or a server-side failure; an offline or rate-limited device fails at once. Devices and device triggers are not part of an agent version. A pushed defineDevice declaration reaches the runtime on the agent’s next turn whether or not you publish it; --auto-deploy only records it as the active version. A standalone defineDeviceTrigger runs only after its pushed version is published, with --auto-deploy or the push prompt. lua deploy has no device types. lua devices list|status|enable|disable|remove|test|test-trigger --device-name <name> manage registered devices from the CLI: enable and disable admit or refuse the device’s connection, and test-trigger relays a test event to the device without running a handler. The client libraries are @lua-ai-global/device-client for Node (Socket.IO and MQTT, with CDN uploads), lua-device-client on PyPI for Python (MQTT), and a single lua_device.py file for MicroPython that runs on a Raspberry Pi Pico W.

Devices and webhooks

A webhook is a request your code handles once; the sender needs nothing beyond the signature and gets an HTTP response. A device is a standing, authenticated connection in both directions: the agent can call the device, the device can wake the agent, and liveness is known at every moment. Use a device when the agent must act on the hardware or the hardware sends more than an occasional event; use a webhook when a system posts you data.

When to use it

  • The agent should control hardware or a local machine from a conversation: expose commands.
  • Sensors or a desktop should wake the agent: send device triggers and push a handler.
  • The hardware is a microcontroller: use the MicroPython client over MQTT.
  • Don’t use a device for a service that already has an HTTP API; call it from a tool with fetch.

Limits

Next steps

Devices quickstart

Connect a first device and run a command from chat.

How devices work

The command and trigger path end to end.

defineDevice reference

Commands, triggers, groups, and handlers.

Reliability and limits

Reconnection, heartbeats, and payload limits.