> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lusor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Shell

> Run a restricted Android shell command on a Lusor Phone, as a last resort.

`POST …/shell` runs one Android shell command on a Lusor Phone. Use it only when no [direct-control action](https://docs.lusor.ai/phones/direct-control.md) does the job.

```bash cURL theme={null}
curl https://api.lusor.ai/v1/workspaces/$WS/devices/$DEVICE/shell \
  -H "X-Api-Key: $LUSOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "command": "input keyevent 224" }'
```

```python Python theme={null}
import os, requests

URL = f"https://api.lusor.ai/v1/workspaces/{os.environ['WS']}/devices/{os.environ['DEVICE']}/shell"
r = requests.post(URL, headers={"X-Api-Key": os.environ["LUSOR_API_KEY"]},
                  json={"command": "input keyevent 224"})
print(r.status_code, r.json())
```

```javascript Node.js theme={null}
const URL = `https://api.lusor.ai/v1/workspaces/${process.env.WS}/devices/${process.env.DEVICE}/shell`;
const r = await fetch(URL, {
  method: "POST",
  headers: { "X-Api-Key": process.env.LUSOR_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ command: "input keyevent 224" }),
});
console.log(r.status, await r.json());
```

`command` is 1 to 4096 characters.

## No output

The response says whether the command ran. It does **not** include the command's output.

| Field | Meaning |
| - | - |
| `ok` | Whether the command ran successfully. |
| `status` | The command's status. |
| `commandId` | An id for this command. |
| `durationMs` | How long it took, when known. |

To see the effect of a command, read the screen with `GET …/state` or `GET …/screenshot`.

## Refused commands

Every command is checked against a list of refused patterns before it is sent. A refused command returns `403` with `error: "denied"` and a message naming the reason. It never reaches the phone.

Refused anywhere in the command:

* Shell operators: `|`, `;`, `&`, `$(` and backticks. Send one command per request.

Refused commands include:

* Restarting or resetting the phone (`reboot`, `wipe`, factory reset) and changing its power state.
* `su`, and starting another shell such as `bash` or `busybox`.
* Recursive deletes (`rm -r` in any form), raw disk writes, formatting, and remounting the system as writable.
* Installing packages (`pm install`), and removing the Lusor app.
* Changing read-only system properties, global settings or content providers, and raw service calls.
* Network tools such as `nc`, `telnet` and `ssh`.

The list may grow. Build for a `403` on any command.

## Audit

Every attempt is recorded, whether it ran or was refused, with the workspace, the API key, the phone and the full command. Do not put secrets in commands.

## Billing

On a Lusor Phone, a shell command with an API key uses Lusor Phone minutes like any other direct action, and can be refused with the same `402`, `409` and `503` errors. See [Billing](https://docs.lusor.ai/phones/direct-control.md#billing).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.