Devices

Devices

An agent can ask a device to do one thing — read something on it, change a setting, take a photo, send a message from it — and wait for the answer. The mesh rings the device through a notifier, the device joins a room, the agent's task goes into the room, and the device's answer comes back to the agent as an outcome it can read.

Devices are off by default. An agent gets them with one block.

The device block

agents: phone_helper: name: "phone_helper" model: "gpt-4o-mini" prompt: | Help the customer with their phone. Use device_action to read or change something on it when they ask you to. device: connection: "my_phones" # a notifier connection (required) room: "my_rooms" # a room connection (optional) approval: "ask" # ask (default) | silent
FieldDefaultMeaning
connection— (required)A notifier record under Infrastructure → Notifiers ([Connections & the Infra Page](../integrations/connections-and-infra)) that rings the device so it joins a room.
roomLeafCraft roomsA room record (Infrastructure → Rooms) where the mesh and the device meet. Leave it out to use LeafCraft rooms.
approvalaskask: the device shows a notification asking to allow the connection, and nothing runs unless it is allowed. silent: the device joins without asking.

An agent with this block gets the device_action tool. The block is strict: an unknown key is refused at load.

Which device: the conversation brings it

The device is never in config. The request that starts or continues a conversation carries it:

{"message": "Is my battery OK?", "device": {"id": "phone-7", "push_token": "..."}} {"message": "...", "devices": [{"id": "phone-7", "push_token": "..."}, {"id": "tablet-2", "push_token": "..."}]}
FieldMeaning
idThe device's id: up to 128 letters, digits and the characters _ - . : @ (and the vertical bar).
push_tokenWhere the notifier reaches this device — an FCM registration token, an APNs device token, a phone number in E.164 for Twilio SMS, and so on.
name, platformOptional; shown to the agent so it knows which device is which.

The push token is kept in the session's storage and taken out of the payload before anything else sees it, so no prompt, history, trace or log line holds it. It expires with the session. Up to 16 devices per conversation.

Notifiers

A notifier is how the mesh rings a device. Add one under Infrastructure → Notifiers; the fields below are what each one asks for (* = required).

ProviderWhat it needs
fcm — Firebase Cloud MessagingService account key (JSON)*, Project ID
mqtt — MQTT brokerBroker address (for devices), Username, Password — or Client certificate (PEM) and Client key (PEM) for brokers that authenticate by certificate; CA certificate (PEM) only for a private CA; Room topic prefix only when it is also the room
apns — Apple Push (APNs)Signing key (.p8)*, Key ID*, Team ID*, Bundle ID*, Sandbox
webpush — Web Push (browsers)VAPID private key*, Contact*
hms — Huawei Push KitClient secret*, Client ID*
wns — Windows Push (WNS)Client secret*, Directory (tenant) ID*, Application (client) ID*
expo — Expo PushAccess token (only when the project turns on enhanced security for push)
onesignal — OneSignalApp API key*, App ID*
azure_iot_hub — Azure IoT HubService connection string* (the device answers a direct method named leafmesh_connect)
aws_iot — AWS IoT CoreDevice data endpoint*, Access key ID*, Secret access key*, Region, Session token
sns — Amazon SNS Mobile PushRegion*, Access key ID*, Secret access key*, Session token
azure_notification_hubs — Azure Notification HubsConnection string*, Hub name*, Payload format (fcmv1, apple or template)
pusher_beams — Pusher BeamsInstance ID*, Primary key*
twilio_sms — Twilio SMSAccount SID*, From*, Link*, and either Auth token or API key SID + API key secret. The device gets a text with a join link.

Rooms

A room is where the mesh and the device meet for one task. Leave room out to use LeafCraft rooms. To use your own, add a room record under Infrastructure → Rooms:

ProviderWhat it needsSet up on its side first
room — Custom roomRoom service URL*, API key*A room service you run, built to the LeafMesh room protocol. Enter its address and the mesh's API key for it.
mqtt — MQTT brokerThe same MQTT connection as the notifier, with Room topic prefixYour broker's access rules must let the mesh and the devices use the room topics. One connection is then both notifier and room.
pubnub — PubNubPublish key*, Subscribe key*, Secret key*, Origin (only for a custom origin)In the PubNub Admin Portal, open the app's keyset and turn on Access Manager and Presence.
ably — AblyAPI key*, Endpoint host (only for a custom endpoint)In your Ably app → API Keys, use a key that may publish, subscribe and use presence on leafmesh:* channels (the root key can). Paste the whole key.
pusher — Pusher ChannelsApp ID*, Key*, Secret*, Cluster*In App Settings, turn on Enable client events (the device answers with them). The mesh needs a public https address.
livekit — LiveKitServer URL*, API key*, API secret*In your LiveKit project → Settings → Keys, copy the URL, key and secret. Install the extra: pip install 'leafmesh[livekit]'.

A room service's secret never leaves the mesh: a device gets a token for its one room, made for each ask.

Test on save

Every notifier and room connection is tested when you save it, without ringing any device: the service is asked whether the credential works (for a custom room, a room is opened with the key and closed again). The result is one of active (proven), failing (refused, with the reason) or readable (stored, but the service could not be asked). Run the test again any time with POST /api/infra/connections/{name}/test.

Outcomes

Every device_action call returns an outcome. Only done means it happened; every other outcome carries success: false and an error, so the mesh treats it as a failed call.

OutcomeMeaning
doneThe device did it. data is what it returned — quoted data from outside the mesh, never instructions.
failedThe device tried and reported a failure, gave no answer in time, or the room closed under the task. If the connection was lost after the task was sent, the outcome is failed and the device may have done it.
declinedThe device declined the task.
sent_for_approvalThe task was handed to the device to be approved there; the answer does not come back. The agent says the task is waiting on approval there.
not_joinedThe device was rung but did not join in time — the connection was not allowed (approval: ask), or the device is offline.
unreachableThe device or the room service could not be reached: no room connection, a refused key, a plan limit, a dead push token.
refusedThe request itself was wrong: no such device in this conversation, no action named, or inputs that are not an object.

Approval

approval on the block is about the device's side: with ask, the device must allow each connection. Approval on the mesh's side is routing, like everywhere else in LeafMesh: if a person must agree before an agent touches a device, route the work to a human agent with a can_call condition, and give that human agent the can_call to the device agent. See HITL Workflows.

Next Steps


LeafMesh — agents that can ask a device to act

Last updated: 11/10/2026