Connect your first device
A Node device with one command, in five steps.
Create a device credential
Bind a credential to one agent and device, then rotate or revoke it.
Client libraries
The Node client, with the Python and MicroPython clients alongside.
Expose device commands as tools
Declare commands, read results, test by hand.
Handle device triggers
Fire an event and run a handler on the platform.
Upload files from a device
Send a screenshot or log to the CDN and hand its URL to the agent.
Reliability and limits
Reconnection, heartbeats, offline semantics, and every cap.
Examples
A Pico W, a laptop, a warehouse station, a smart office, a factory monitor.
Transports
Two transports carry the same messages. The Node client uses Socket.IO over WebSocket tohttps://api.heylua.ai by default. The Node client with transport: 'mqtt', the Python client, and the MicroPython client use MQTT 3.1.1 over WebSocket with TLS on port 443, at wss://mqtt.heylua.ai/mqtt. MQTT adds a retained status message and a last-will message; see the MQTT protocol reference.
The credential handshake
A Socket.IO client sends its device credential, the agent ID, the device name, an optional group, and its command list in the connection’sauth object. An MQTT client sends <agentId>:<deviceName> as the username and the credential as the password, then publishes an online status message carrying the command list.
The platform accepts the connection only when the credential’s binding matches the agent ID and device name exactly and every granted operation (commands, triggers, assets.upload) is permitted on that agent. A legacy API key connects when its owner may manage the agent; a scoped personal API key is refused. On success the platform replies with connected and stores the command list.
Authorization continues after connect: revoking, rotating, or suspending a device credential closes its Socket.IO connections at once with AUTH_FAILED and denies an MQTT device’s next publish or subscribe; as a fallback, the credential is re-checked about every minute.
Commands as tools
The stored command list is validated on arrival: name grammar, at most 128 commands, 4 KB per input schema, as listed in Expose device commands as tools. On every turn the platform builds one tool per command of each online device, nameddevice__<device>__<command> with hyphens in the device name turned into underscores, plus device__<device>__is_online. A device declared with defineDevice keeps its tools while offline; calls to it return DEVICE_OFFLINE.
A call is routed to the server holding the connection and delivered as a command message. The device runs its handler and replies with response, which the platform returns as the tool result. It waits the command’s timeout (30 seconds by default) plus two seconds, then reports TIMEOUT. The Node and Python clients remember recent command IDs for five minutes and answer a redelivery with the cached reply instead of running the handler again; the MicroPython client ignores the duplicate. A command to an offline device fails at once with DEVICE_OFFLINE.
Triggers as handler runs
The device sends{ triggerName, payload }; the platform checks the credential’s triggers operation, the 1 MB payload cap, and the limit of 10 triggers per second per agent, then queues the event and replies with trigger_ack { triggerId, received: true }. The acknowledgment means queued, not handled.
The name is matched, by exact string, first against the triggers of a defineDevice declaration for that device, then against standalone defineDeviceTrigger declarations on the agent. The matching execute runs in an isolated runtime with the payload and { device: { name }, trigger: { name, triggerId } } and has up to 10 minutes to finish, the platform’s default cap for event handlers; a returned { startWorkflow } starts a workflow run. Events are delivered at least once from a durable queue: an event with no handler is logged and dropped, and a handler that throws or times out is run again, up to 3 attempts 60 seconds apart, so handlers must be idempotent.
The platform fires device_connected and device_disconnected itself when a connection opens or closes. It never sends trigger_result back to the device, so the clients’ trigger-result handlers don’t run.
Heartbeats and offline detection
Every client sends a heartbeat every 30 seconds and the platform records it as the device’s last-seen time. A device is marked offline immediately when its socket closes or, over MQTT, when the broker publishes its will. Otherwise a sweep that runs every five minutes marks any device whose last heartbeat is older than five minutes offline, so a dead connection is detected within five to ten minutes. The stored command list of a self-describing device 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. Offline means removed from the online set with the stored command list cleared. On the next turn a self-describing device’s command tools are gone anddevice__<device>__is_online answers { status: 'offline' }. device_disconnected fires only when the connection closes, not when the sweep times a device out.
Reconnection
The Node Socket.IO client retries with a jittered delay that doubles from 1 to 30 seconds, without limit, except afterAUTH_FAILED or MISSING_AUTH (terminal) and RATE_LIMITED, after which it waits the delay the platform advertises. A connection the platform closes itself, for example with AGENT_FORBIDDEN or DEVICE_DISABLED, is retried every second without limit, so handle the client’s error event. The MQTT clients retry on their own schedules: Node every 5 seconds, Python from 1 to 120 seconds, MicroPython from 2 to 30 seconds with a reboot after ten failures. Every reconnection repeats the handshake, so the command list is re-sent and the tools return.
Next steps
MQTT protocol
Topics, payloads, QoS, and error codes.
defineDevice and defineDeviceTrigger
Declare a device, its commands, and its triggers in code.

