Camera Capture on iOS, Android, macOS, and Linux Nodes

Learn how to enable and use camera capture on iOS, Android, macOS, and Linux nodes for photos and short video clips via Gateway node.invoke commands.

Read this when

  • Adding or modifying camera capture on node platforms
  • Extending agent-accessible MEDIA temp-file workflows

OpenClaw enables camera capture for agent workflows on paired iOS, Android, macOS, and Linux nodes. Through Gateway node.invoke, you can take a photo (jpg) or record a short video clip (mp4, optionally with audio).

A user-controlled, platform-specific setting must be enabled before any camera access is granted.

iOS node

iOS user setting

  • Go to the iOS Settings tab, select Camera, and turn on Allow Camera (camera.enabled).
    • Default: on (if the key is missing, access is treated as enabled).
    • When disabled: camera.* commands return CAMERA_DISABLED.

iOS commands (via Gateway node.invoke)

  • camera.list

    • The response payload is devices, an array of { id, name, position, deviceType }.
  • camera.snap

    • Parameters:
      • facing: front|back (defaults to front)
      • maxWidth: number (optional; default 1600)
      • quality: 0..1 (optional; defaults to 0.9, clamped to [0.05, 1.0])
      • format: currently jpg
      • delayMs: number (optional; defaults to 0, internally capped at 10000)
      • deviceId: string (optional; chosen from camera.list)
    • The response payload includes format: "jpg", base64, width, and height.
    • Payload guard: photos are recompressed so the base64-encoded payload stays under 5MB.
  • camera.clip

    • Parameters:
      • facing: front|back (defaults to front)
      • durationMs: number (default 3000, clamped to [250, 60000])
      • includeAudio: boolean (default true)
      • format: currently mp4
      • deviceId: string (optional; chosen from camera.list)
    • The response payload consists of format: "mp4", base64, durationMs, and hasAudio.

iOS foreground requirement

As with canvas.*, the iOS node restricts camera.* commands to the foreground. If invoked in the background, they return NODE_BACKGROUND_UNAVAILABLE.

CLI helper

The simplest approach for obtaining media files is using the CLI helper. It writes decoded media to a temporary file and then prints the saved path.

openclaw nodes camera snap --node <id>                 # default: both front + back (2 MEDIA lines)
openclaw nodes camera snap --node <id> --facing front
openclaw nodes camera clip --node <id> --duration 3000
openclaw nodes camera clip --node <id> --no-audio

nodes camera snap is set to --facing both by default, which records both the front and rear cameras so the agent sees both angles. To change this, supply --device-id with a specific facing direction. If --device-id is active, both is not accepted. Unless you create a custom wrapper, output files are stored temporarily in the operating system's temp directory.

Android node

Android user setting

  • Android Settings sheet → CameraAllow Camera (camera.enabled).
    • Fresh installs default to off. For existing installations created before this setting was introduced, the value is migrated to on so that upgrades do not silently disable previously working camera access.
    • When turned off: camera.* commands produce CAMERA_DISABLED: enable Camera in Settings.

Permissions

  • CAMERA is mandatory for both camera.snap and camera.clip. If the permission is missing or denied, CAMERA_PERMISSION_REQUIRED is returned.
  • RECORD_AUDIO is mandatory for camera.clip when includeAudio is true. If the permission is missing or denied, MIC_PERMISSION_REQUIRED is returned.

When possible, the app requests runtime permissions.

Android foreground requirement

Similar to canvas.*, the Android node permits camera.* commands only while in the foreground. If invoked in the background, NODE_BACKGROUND_UNAVAILABLE: command requires foreground is returned.

Android commands (via Gateway node.invoke)

  • camera.list

    • Response payload: devices, an array of { id, name, position, deviceType }.
  • camera.snap

    • Params: facing (front|back, default front), quality (default 0.95, clamped to [0.1, 1.0]), maxWidth (default 1600), deviceId (optional; an unknown id fails with INVALID_REQUEST).
    • Response payload: format: "jpg", base64, width, height.
    • Payload guard: recompressed so the base64 stays under 5MB (the same budget as iOS).
  • camera.clip

    • Params: facing (default front), durationMs (default 3000, clamped to [200, 60000]), includeAudio (default true), deviceId (optional).
    • Response payload: format: "mp4", base64, durationMs, hasAudio.
    • Payload guard: raw MP4 is capped at 18MB before base64 encoding. Clips that exceed this limit fail with PAYLOAD_TOO_LARGE. Reduce durationMs and try again.

macOS app

macOS user setting

The macOS companion app includes a checkbox:

  • Settings → General → Allow Camera (openclaw.cameraEnabled).
    • Default: off.
    • When this is off, camera requests produce CAMERA_DISABLED: enable Camera in Settings.

CLI helper (node invoke)

Run camera commands on the macOS node using the main openclaw CLI.

openclaw nodes camera list --node <id>                     # list camera ids
openclaw nodes camera snap --node <id>                     # prints saved path
openclaw nodes camera snap --node <id> --max-width 1280
openclaw nodes camera snap --node <id> --delay-ms 2000
openclaw nodes camera snap --node <id> --device-id <id>
openclaw nodes camera clip --node <id> --duration 10s       # prints saved path
openclaw nodes camera clip --node <id> --duration-ms 3000   # prints saved path (legacy flag)
openclaw nodes camera clip --node <id> --device-id <id>
openclaw nodes camera clip --node <id> --no-audio
  • openclaw nodes camera snap uses maxWidth=1600 as its default unless a different value is specified.
  • After warm-up and exposure settling, camera.snap pauses for delayMs (default 2000ms, capped at [0, 10000]) before taking the capture.
  • Photo payloads undergo recompression so the base64 size stays under 5MB.

Linux node host

Camera capture is added to the CLI openclaw node service by the bundled Linux Node plugin. It operates on a headless host and does not need the Linux desktop application.

Camera access is disabled by default. Turn it on in the plugin entry, then restart the node service so its Gateway advertisement is regenerated:

{
  plugins: {
    entries: {
      "linux-node": {
        config: {
          camera: { enabled: true },
        },
      },
    },
  },
}

Prerequisites:

  • FFmpeg compiled with V4L2 input, libx264, and AAC support
  • a /dev/video* device that the node-service user can read; on most distributions, add that user to the video group
  • for clips using the default includeAudio: true, a working PulseAudio server or PipeWire PulseAudio compatibility layer with a default source

On Linux, capture-capable readable V4L2 device paths come from camera.list; FFmpeg probes each /dev/video* candidate and skips metadata or output-only nodes. Because device position is unknown, facing requests that lack deviceId return a single unknown-position photo or clip rather than a front or back camera. When a host has multiple cameras, use deviceId. camera.snap applies FFmpeg input warm-up for delayMs and maintains the aspect ratio while constraining width. camera.clip captures microphone audio as the MP4 audio track; OpenClaw intentionally provides no separate microphone command.

The plugin relies on libx264 for MP4 video and does not silently switch codecs. An FFmpeg build that lacks the necessary input or encoders returns CAMERA_UNAVAILABLE. Photos and clips that would exceed the 25MB base64 payload limit fail with PAYLOAD_TOO_LARGE.

camera.snap and camera.clip are dangerous commands. Add them to gateway.nodes.commands.allow only when you plan to arm capture; enabling the plugin alone does not bypass Gateway policy.

Safety + practical limits

  • Camera and microphone access trigger standard OS permission prompts and require usage strings in Info.plist.
  • Video clips are limited to 60s to prevent oversized node payloads due to base64 overhead and message limits.

macOS screen video (OS-level)

For screen video (not camera), use the macOS companion:

openclaw nodes screen record --node <id> --duration 10s --fps 15   # prints saved path

Requires macOS Screen Recording permission (TCC).