---
title: Commands, capabilities and events
description: Describe what a gadget can do, invoke it through the account boundary, and report what happens.
---

The device contract has three parts. **Commands** are callable operations. **Capabilities** summarize the available hardware. **Events** report observations. None of these grants the caller more permission than the platform provides.

## Register an exported interface

At connection, `link.register` includes `commands_v2` and `capabilities`. Each command has a name, description and required/optional parameter definitions. This example illustrates the registered description of a text-display operation:

```json
{
  "commands_v2": {
    "display.show_text": {
      "description": "Show a short text caption on this display.",
      "required": {
        "text": { "type": "string", "description": "Caption text." }
      },
      "optional": {}
    }
  },
  "capabilities": {
    "screen": { "width": 240, "height": 240, "color": true },
    "speaker": true
  }
}
```

This is a partial registration example, not a complete handshake. The SDK fills the transport and device identity fields. Follow the [capability contract](https://github.com/impoai/impo-gadget-sdk/blob/main/CAPABILITIES.md) for each command's real limits.

## Implement a command

ESP32 board-specific commands use `impo_board_t.commands` and `impo_command_t` from `impo_commands.h`. Describe parameters, validate their values in the handler, and return a structured success or error. The handler runs on the Link session task and must not block it; long work needs the runtime's asynchronous pattern.

Use a board-specific command first when experimenting. Promote an operation to the shared vocabulary when multiple boards can implement the same semantics. Keep names and result meanings consistent across boards.

## Invoke through Impo

The personal agent first calls `impo_list_gadgets`, then selects a returned gadget and command. The server derives the account from the durable invocation, verifies that the device is owned and online, checks its registered command and calls the gateway.

The gateway sends `link.invoke`; the device responds with `link.result`. The application adapter withholds `system.*`, `link.*`, `device.ota` and `device.unpair` from model invocation. Makers cannot override that policy through a command description.

`impo_gadget_command` does not automatically retry side effects. If transport fails after a movement or audio request, the outcome may be unknown. Report that uncertainty rather than assume the operation never ran.

## Emit an event

The control stream accepts `link.event` with an event name and data. For example, the event parameters for picking up a device can be:

```json
{
  "event": "picked_up",
  "data": { "orientation": "upright" }
}
```

Debounce noisy sensor signals in the device. The gateway also applies a minimum interval guard and retains recent event records. Current events become main-chat messages with the `[Gadget event]` prefix; the reply is spoken by that gadget.

The [proposed gadget-agent router](/docs/gadgets/gadget-agents/) would match structured events to saved behavior. That endpoint and subscription model are not available today.

## Verify the outcome you report

`speaker.say` can return an accepted/fetching state before audio finishes; use the speaker status contract to distinguish playback failure. A chassis command can prove that bytes were acknowledged without proving physical movement.

Test registration, malformed parameters, cross-account requests, offline devices, reconnects and unknown outcomes. Then verify the actual display, sound, sensor or actuator. Source references: the [server tool](https://github.com/impoai/impo/blob/main/server/src/tools/gadget-tools.ts) and [gateway device hub](https://github.com/impoai/impo/blob/main/server/gadget-gateway/device-hub.mjs).
