{
  "title": "Camera capture (agent)",
  "content": "OpenClaw supports **camera capture** for agent workflows:\n\n* **iOS node** (paired via Gateway): capture a **photo** (`jpg`) or **short video clip** (`mp4`, with optional audio) via `node.invoke`.\n* **Android node** (paired via Gateway): capture a **photo** (`jpg`) or **short video clip** (`mp4`, with optional audio) via `node.invoke`.\n* **macOS app** (node via Gateway): capture a **photo** (`jpg`) or **short video clip** (`mp4`, with optional audio) via `node.invoke`.\n\nAll camera access is gated behind **user-controlled settings**.\n\n### User setting (default on)\n\n* iOS Settings tab → **Camera** → **Allow Camera** (`camera.enabled`)\n  * Default: **on** (missing key is treated as enabled).\n  * When off: `camera.*` commands return `CAMERA_DISABLED`.\n\n### Commands (via Gateway `node.invoke`)\n\n* `camera.list`\n  * Response payload:\n    * `devices`: array of `{ id, name, position, deviceType }`\n\n* `camera.snap`\n  * Params:\n    * `facing`: `front|back` (default: `front`)\n    * `maxWidth`: number (optional; default `1600` on the iOS node)\n    * `quality`: `0..1` (optional; default `0.9`)\n    * `format`: currently `jpg`\n    * `delayMs`: number (optional; default `0`)\n    * `deviceId`: string (optional; from `camera.list`)\n  * Response payload:\n    * `format: \"jpg\"`\n    * `base64: \"<...>\"`\n    * `width`, `height`\n  * Payload guard: photos are recompressed to keep the base64 payload under 5 MB.\n\n* `camera.clip`\n  * Params:\n    * `facing`: `front|back` (default: `front`)\n    * `durationMs`: number (default `3000`, clamped to a max of `60000`)\n    * `includeAudio`: boolean (default `true`)\n    * `format`: currently `mp4`\n    * `deviceId`: string (optional; from `camera.list`)\n  * Response payload:\n    * `format: \"mp4\"`\n    * `base64: \"<...>\"`\n    * `durationMs`\n    * `hasAudio`\n\n### Foreground requirement\n\nLike `canvas.*`, the iOS node only allows `camera.*` commands in the **foreground**. Background invocations return `NODE_BACKGROUND_UNAVAILABLE`.\n\n### CLI helper (temp files + MEDIA)\n\nThe easiest way to get attachments is via the CLI helper, which writes decoded media to a temp file and prints `MEDIA:<path>`.\n\n* `nodes camera snap` defaults to **both** facings to give the agent both views.\n* Output files are temporary (in the OS temp directory) unless you build your own wrapper.\n\n### User setting (default on)\n\n* Android Settings sheet → **Camera** → **Allow Camera** (`camera.enabled`)\n  * Default: **on** (missing key is treated as enabled).\n  * When off: `camera.*` commands return `CAMERA_DISABLED`.\n\n* Android requires runtime permissions:\n  * `CAMERA` for both `camera.snap` and `camera.clip`.\n  * `RECORD_AUDIO` for `camera.clip` when `includeAudio=true`.\n\nIf permissions are missing, the app will prompt when possible; if denied, `camera.*` requests fail with a\n`*_PERMISSION_REQUIRED` error.\n\n### Foreground requirement\n\nLike `canvas.*`, the Android node only allows `camera.*` commands in the **foreground**. Background invocations return `NODE_BACKGROUND_UNAVAILABLE`.\n\nPhotos are recompressed to keep the base64 payload under 5 MB.\n\n### User setting (default off)\n\nThe macOS companion app exposes a checkbox:\n\n* **Settings → General → Allow Camera** (`openclaw.cameraEnabled`)\n  * Default: **off**\n  * When off: camera requests return “Camera disabled by user”.\n\n### CLI helper (node invoke)\n\nUse the main `openclaw` CLI to invoke camera commands on the macOS node.\n\n* `openclaw nodes camera snap` defaults to `maxWidth=1600` unless overridden.\n* On macOS, `camera.snap` waits `delayMs` (default 2000ms) after warm-up/exposure settle before capturing.\n* Photo payloads are recompressed to keep base64 under 5 MB.\n\n## Safety + practical limits\n\n* Camera and microphone access trigger the usual OS permission prompts (and require usage strings in Info.plist).\n* Video clips are capped (currently `<= 60s`) to avoid oversized node payloads (base64 overhead + message limits).\n\n## macOS screen video (OS-level)\n\nFor *screen* video (not camera), use the macOS companion:\n\n* Requires macOS **Screen Recording** permission (TCC).",
  "code_samples": [
    {
      "code": "Notes:\n\n* `nodes camera snap` defaults to **both** facings to give the agent both views.\n* Output files are temporary (in the OS temp directory) unless you build your own wrapper.\n\n## Android node\n\n### User setting (default on)\n\n* Android Settings sheet → **Camera** → **Allow Camera** (`camera.enabled`)\n  * Default: **on** (missing key is treated as enabled).\n  * When off: `camera.*` commands return `CAMERA_DISABLED`.\n\n### Permissions\n\n* Android requires runtime permissions:\n  * `CAMERA` for both `camera.snap` and `camera.clip`.\n  * `RECORD_AUDIO` for `camera.clip` when `includeAudio=true`.\n\nIf permissions are missing, the app will prompt when possible; if denied, `camera.*` requests fail with a\n`*_PERMISSION_REQUIRED` error.\n\n### Foreground requirement\n\nLike `canvas.*`, the Android node only allows `camera.*` commands in the **foreground**. Background invocations return `NODE_BACKGROUND_UNAVAILABLE`.\n\n### Payload guard\n\nPhotos are recompressed to keep the base64 payload under 5 MB.\n\n## macOS app\n\n### User setting (default off)\n\nThe macOS companion app exposes a checkbox:\n\n* **Settings → General → Allow Camera** (`openclaw.cameraEnabled`)\n  * Default: **off**\n  * When off: camera requests return “Camera disabled by user”.\n\n### CLI helper (node invoke)\n\nUse the main `openclaw` CLI to invoke camera commands on the macOS node.\n\nExamples:",
      "language": "unknown"
    },
    {
      "code": "Notes:\n\n* `openclaw nodes camera snap` defaults to `maxWidth=1600` unless overridden.\n* On macOS, `camera.snap` waits `delayMs` (default 2000ms) after warm-up/exposure settle before capturing.\n* Photo payloads are recompressed to keep base64 under 5 MB.\n\n## Safety + practical limits\n\n* Camera and microphone access trigger the usual OS permission prompts (and require usage strings in Info.plist).\n* Video clips are capped (currently `<= 60s`) to avoid oversized node payloads (base64 overhead + message limits).\n\n## macOS screen video (OS-level)\n\nFor *screen* video (not camera), use the macOS companion:",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h2",
      "text": "iOS node",
      "id": "ios-node"
    },
    {
      "level": "h3",
      "text": "User setting (default on)",
      "id": "user-setting-(default-on)"
    },
    {
      "level": "h3",
      "text": "Commands (via Gateway `node.invoke`)",
      "id": "commands-(via-gateway-`node.invoke`)"
    },
    {
      "level": "h3",
      "text": "Foreground requirement",
      "id": "foreground-requirement"
    },
    {
      "level": "h3",
      "text": "CLI helper (temp files + MEDIA)",
      "id": "cli-helper-(temp-files-+-media)"
    },
    {
      "level": "h2",
      "text": "Android node",
      "id": "android-node"
    },
    {
      "level": "h3",
      "text": "User setting (default on)",
      "id": "user-setting-(default-on)"
    },
    {
      "level": "h3",
      "text": "Permissions",
      "id": "permissions"
    },
    {
      "level": "h3",
      "text": "Foreground requirement",
      "id": "foreground-requirement"
    },
    {
      "level": "h3",
      "text": "Payload guard",
      "id": "payload-guard"
    },
    {
      "level": "h2",
      "text": "macOS app",
      "id": "macos-app"
    },
    {
      "level": "h3",
      "text": "User setting (default off)",
      "id": "user-setting-(default-off)"
    },
    {
      "level": "h3",
      "text": "CLI helper (node invoke)",
      "id": "cli-helper-(node-invoke)"
    },
    {
      "level": "h2",
      "text": "Safety + practical limits",
      "id": "safety-+-practical-limits"
    },
    {
      "level": "h2",
      "text": "macOS screen video (OS-level)",
      "id": "macos-screen-video-(os-level)"
    }
  ],
  "url": "llms-txt#camera-capture-(agent)",
  "links": []
}