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

# Get Started with Tapo Cameras

> Install a Seam SDK, connect a Tapo camera through a Seam Bridge, retrieve the camera device, and receive motion events. Beta.

## Overview

Seam provides a universal API to connect and control many brands of devices. This guide provides a rapid introduction to connecting your [Tapo](/docs/device-and-system-integration-guides/tapo-cameras) cameras to Seam, retrieving them as devices, and receiving their motion events. Seam reaches Tapo cameras on the local network through a [Seam Bridge](/docs/capability-guides/seam-bridge).

To learn more about other device brands supported by Seam, head over to our [integration page](https://www.seam.co/supported-devices-and-systems).

<Warning>
  Tapo cameras are in **beta**. Seam turns on Tapo, camera events, event media, and live video for each workspace separately, so [contact Seam](mailto:support@seam.co) to enable the ones you need for your workspace, including your sandbox workspace, before you follow this guide.
</Warning>

***

## 1 — Install Seam SDK

Seam provides client libraries for many languages, such as JavaScript, Python, Ruby, PHP, and others, as well as a Postman collection and [OpenAPI](https://connect.getseam.com/openapi.json) spec.

* JavaScript / TypeScript ([npm](https://www.npmjs.com/package/seam), [GitHub](https://github.com/seamapi/javascript))
* Python ([pip](https://pypi.org/project/seam/), [GitHub](https://github.com/seamapi/python))
* Ruby Gem ([rubygem](https://rubygems.org/gems/seam), [GitHub](https://github.com/seamapi/ruby))
* PHP ([packagist](https://packagist.org/packages/seamapi/seam), [GitHub](https://github.com/seamapi/php))
* C# ([nuget](https://www.nuget.org/packages/Seam), [GitHub](https://github.com/seamapi/csharp))
* Java ([Maven](https://central.sonatype.com/artifact/co.seam/java), [GitHub](https://github.com/seamapi/java))

<CodeGroup>
  ```bash JavaScript theme={"dark"}
  npm i seam
  ```

  ```bash Python theme={"dark"}
  pip install seam
  # For some development environments, use pip3 in this command instead of pip.
  ```

  ```bash Ruby theme={"dark"}
  bundle add seam
  ```

  ```bash PHP theme={"dark"}
  composer require seamapi/seam
  ```

  ```bash C# theme={"dark"}
  Install using nuget: https://www.nuget.org/packages/Seam
  ```

  ```bash Java theme={"dark"}
  // Add to your pom.xml or build.gradle — see Maven Central for details.
  ```

  ```bash cURL (bash) theme={"dark"}
  # cURL is already installed on most systems. No additional installation needed.
  # Export your API key as an environment variable:
  export SEAM_API_KEY=seam_test2ZTo_0mEYQW2TvNDCxG5Atpj85Ffw
  ```
</CodeGroup>

Once installed, [sign up for Seam](https://console.seam.co/) to get your API key, and export it as an environment variable:

```bash theme={"dark"}
export SEAM_API_KEY=seam_test2ZTo_0mEYQW2TvNDCxG5Atpj85Ffw
```

<Info>
  This guide uses a Sandbox Workspace. Only virtual devices can be connected. If
  you need to connect a real Tapo camera, use a non-sandbox workspace and API
  key.
</Info>

***

## 2 — Link a Tapo Camera with Seam

To use a Tapo camera through the Seam API, you must first authorize your Seam workspace to reach the camera. To do so, Seam provides [Connect Webviews](/docs/core-concepts/connect-webviews): pre-built UX flows that walk you through pairing a Seam Bridge and entering the camera's connection details.

Tapo is a beta provider, so include `tapo` in the Connect Webview's `accepted_providers` list. The Tapo login in the Connect Webview works only after Seam turns on Tapo for your workspace, including your sandbox workspace.

#### Request a Connect Webview

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  import { Seam } from 'seam'

  const seam = new Seam()

  const connectWebview = await seam.connectWebviews.create({
    accepted_providers: ['tapo'],
  })

  console.log(connectWebview.login_successful) // false

  // Send the webview URL to your user
  console.log(connectWebview.url)
  ```

  ```python Python theme={"dark"}
  from seam import Seam

  seam = Seam()

  webview = seam.connect_webviews.create(
      accepted_providers=["tapo"]
  )

  assert webview.login_successful is False

  # Send the webview URL to your user
  print(webview.url)
  ```

  ```ruby Ruby theme={"dark"}
  require "seam"

  seam = Seam.new()

  webview = seam.connect_webviews.create(
    accepted_providers: ["tapo"]
  )

  puts webview.login_successful # false

  # Send the webview URL to your user
  puts webview.url
  ```

  ```php PHP theme={"dark"}
  <?php
  use Seam\SeamClient;

  $seam = new SeamClient("YOUR_API_KEY");

  $webview = $seam->connect_webviews->create(
    accepted_providers: ["tapo"]
  );

  echo $webview->login_successful; // false

  // Send the webview URL to your user
  echo $webview->url;
  ```

  ```csharp C# theme={"dark"}
  using Seam.Client;

  var seam = new SeamClient(apiToken: "YOUR_API_KEY");

  var webview = seam.ConnectWebviews.Create(
    acceptedProviders: new List<string> { "tapo" }
  );

  Console.WriteLine(webview.LoginSuccessful); // false

  // Send the webview URL to your user
  Console.WriteLine(webview.Url);
  ```

  ```java Java theme={"dark"}
  import co.seam.Seam;
  import co.seam.api.types.ConnectWebview;

  Seam seam = Seam.builder().apiKey("YOUR_API_KEY").build();

  ConnectWebview webview = seam.connectWebviews().create(
    ConnectWebviewsCreateRequest.builder()
      .acceptedProviders(List.of("tapo"))
      .build()
  );

  System.out.println(webview.getLoginSuccessful()); // false

  // Send the webview URL to your user
  System.out.println(webview.getUrl());
  ```

  ```bash cURL (bash) theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/connect_webviews/create' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "accepted_providers": ["tapo"]
    }'
  ```
</CodeGroup>

#### Authorize Your Workspace

Navigate to the URL returned by the Webview object. Since you are using a sandbox workspace, you don't need to install Seam Bridge. Continue past the Seam Bridge instructions, submit the prefilled sandbox pairing token, and then enter the Tapo [sandbox camera](./sandbox-tapo-cameras) details below:

* **Camera address:** camera.test
* **ONVIF port:** 2020
* **Camera Account username:** tapo-test-user
* **Camera Account password:** tapo-test-password
* **Tapo account password (optional):** tapo-sandbox-account-password

Confirm the Connect Webview was successful by querying its status:

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const updatedWebview = await seam.connectWebviews.get(
    connectWebview.connect_webview_id,
  )

  console.log(updatedWebview.login_successful) // true
  ```

  ```python Python theme={"dark"}
  updated_webview = seam.connect_webviews.get(
      connect_webview_id=webview.connect_webview_id
  )

  assert updated_webview.login_successful  # true
  ```

  ```ruby Ruby theme={"dark"}
  updated_webview = seam.connect_webviews.get(
    connect_webview_id: webview.connect_webview_id
  )

  puts updated_webview.login_successful # true
  ```

  ```php PHP theme={"dark"}
  <?php
  $updated_webview = $seam->connect_webviews->get(
    connect_webview_id: $webview->connect_webview_id
  );

  echo $updated_webview->login_successful; // true
  ```

  ```csharp C# theme={"dark"}
  var updatedWebview = seam.ConnectWebviews.Get(
    connectWebviewId: webview.ConnectWebviewId
  );

  Console.WriteLine(updatedWebview.LoginSuccessful); // true
  ```

  ```java Java theme={"dark"}
  ConnectWebview updatedWebview = seam.connectWebviews().get(
    ConnectWebviewsGetRequest.builder()
      .connectWebviewId(webview.getConnectWebviewId())
      .build()
  );

  System.out.println(updatedWebview.getLoginSuccessful()); // true
  ```

  ```bash cURL (bash) theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/connect_webviews/get' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"connect_webview_id\": \"${CONNECT_WEBVIEW_ID}\"
    }"
  ```
</CodeGroup>

***

## 3 — Retrieve the Tapo Camera Device

After a Tapo camera is linked with Seam, you can retrieve it as a device. Each Tapo connected account contains exactly one camera, with the `tapo_camera` device type.

The `can_stream_live_video` capability flag is `true` when the camera supports live video and is online. To [stream its live video](/docs/capability-guides/cameras/stream-live-video), live video must also be turned on for your workspace. Otherwise, `/cameras/live_views/create` returns a `camera_not_available` error. The sandbox camera reports `true`, but the sandbox can't stream video, so use a real camera to try live view.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const cameras = await seam.devices.list({ device_type: 'tapo_camera' })

  const camera = cameras[0]

  console.log(camera.properties.online) // true
  console.log(camera.can_stream_live_video) // true

  console.log(camera)
  /*
  {
    device_id: '...',
    device_type: 'tapo_camera',
    can_stream_live_video: true,
    properties: {
      online: true,
      manufacturer: 'tapo',
      name: 'C200',
      tapo_metadata: {
        model: 'C200',
        firmware_version: 'fake-1.0',
        hardware_version: 'TAPO-C200-HARDWARE-001'
      }
    },
    ...
  }
  */
  ```

  ```python Python theme={"dark"}
  cameras = seam.devices.list(device_type="tapo_camera")

  camera = cameras[0]

  assert camera.properties["online"] is True
  assert camera.can_stream_live_video is True

  print(camera)
  # Device(
  #   device_id='...',
  #   device_type='tapo_camera',
  #   can_stream_live_video=True,
  #   properties={
  #     'online': True,
  #     'manufacturer': 'tapo',
  #     'name': 'C200',
  #     'tapo_metadata': {
  #       'model': 'C200',
  #       'firmware_version': 'fake-1.0',
  #       'hardware_version': 'TAPO-C200-HARDWARE-001'
  #     }
  #   },
  #   ...
  # )
  ```

  ```ruby Ruby theme={"dark"}
  camera = seam.devices.list(device_type: "tapo_camera").first

  puts camera.properties.online # true

  puts camera
  ```

  ```php PHP theme={"dark"}
  <?php
  $cameras = $seam->devices->list(device_type: "tapo_camera");

  $camera = $cameras[0];

  echo $camera->properties->online; // true
  ```

  ```csharp C# theme={"dark"}
  var cameras = seam.Devices.List(
    deviceType: Seam.Api.Devices.ListRequest.DeviceTypeEnum.TapoCamera
  );

  var camera = cameras[0];

  Console.WriteLine(camera.Properties.Online); // true
  ```

  ```java Java theme={"dark"}
  var cameras = seam.devices().list(
    DevicesListRequest.builder()
      .deviceType("tapo_camera")
      .build()
  );

  var camera = cameras.get(0);

  System.out.println(camera.getProperties().getOnline()); // true
  ```

  ```bash cURL (bash) theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/devices/list' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "device_type": "tapo_camera"
    }'
  ```
</CodeGroup>

***

## 4 — Receive Motion Events

When a connected Tapo camera detects motion, Seam creates a `camera.activated` event with `activation_reason` set to `motion_detected`. To receive these events as they happen, [set up a webhook](/docs/developer-tools/webhooks). You can also [list events](/docs/api/events/list) for the camera.

<Info>
  The Tapo sandbox camera doesn't simulate motion, so this step returns no events in a sandbox workspace. Use a real camera in a non-sandbox workspace to see motion events.
</Info>

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const events = await seam.events.list({
    device_id: camera.device_id,
    event_type: 'camera.activated',
    since: '2026-01-01T00:00:00Z',
  })

  console.log(events)
  ```

  ```python Python theme={"dark"}
  events = seam.events.list(
      device_id=camera.device_id,
      event_type="camera.activated",
      since="2026-01-01T00:00:00Z"
  )

  print(events)
  ```

  ```ruby Ruby theme={"dark"}
  events = seam.events.list(
    device_id: camera.device_id,
    event_type: "camera.activated",
    since: "2026-01-01T00:00:00Z"
  )

  puts events.inspect
  ```

  ```php PHP theme={"dark"}
  <?php
  $events = $seam->events->list(
    device_id: $camera->device_id,
    event_type: "camera.activated",
    since: "2026-01-01T00:00:00Z"
  );

  echo json_encode($events);
  ```

  ```csharp C# theme={"dark"}
  var events = seam.Events.List(
    deviceId: camera.DeviceId,
    eventType: Seam.Api.Events.ListRequest.EventTypeEnum.CameraActivated,
    since: "2026-01-01T00:00:00Z"
  );

  foreach (var cameraEvent in events)
  {
    Console.WriteLine(cameraEvent);
  }
  ```

  ```java Java theme={"dark"}
  var events = seam.events().list(
    EventsListRequest.builder()
      .deviceId(camera.getDeviceId())
      .eventType("camera.activated")
      .since("2026-01-01T00:00:00Z")
      .build()
  );

  System.out.println(events);
  ```

  ```bash cURL (bash) theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/events/list' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"device_id\": \"${DEVICE_ID}\",
      \"event_type\": \"camera.activated\",
      \"since\": \"2026-01-01T00:00:00Z\"
    }"
  ```
</CodeGroup>

**Example `camera.activated` event:**

```json theme={"dark"}
{
  "event_id": "0b7f0d6a-5b1e-4d0c-9d5c-2d8f6a3e1c44",
  "event_type": "camera.activated",
  "workspace_id": "398d80b7-3f96-47c2-b85a-6f8ba21d07be",
  "device_id": "a83690b2-2b70-409a-9a94-426699b84c97",
  "connected_account_id": "c9d3f2a1-7b4e-4f0a-8e2d-5a6b7c8d9e0f",
  "activation_reason": "motion_detected",
  "media_ids": [
    "01a0d985-621c-7b3e-9f2d-4a5b6c7d8e9f",
    "01a0d985-621c-7c4f-8a3e-5b6c7d8e9f0a"
  ],
  "video_url": "https://connect.getseam.com/media/get?media_id=01a0d985-621c-7b3e-9f2d-4a5b6c7d8e9f&format=redirect",
  "image_url": "https://connect.getseam.com/media/get?media_id=01a0d985-621c-7c4f-8a3e-5b6c7d8e9f0a&format=redirect",
  "occurred_at": "2026-09-25T17:03:12.000Z",
  "created_at": "2026-09-25T17:03:12.412Z"
}
```

The event includes `media_ids` when event media is turned on for your workspace. `video_url` and `image_url` point to `/media/get` with `format=redirect`, and are included for compatibility. Use `media_ids` in new integrations.

***

## 5 — Retrieve Event Recordings

If you enter the Tapo account password when you connect a real camera, Seam retrieves the camera's microSD recording of each motion event. The event's `media_ids` list a video clip and a thumbnail, which you fetch with [`/media/get`](/docs/api/media/get). The returned `url` expires within minutes, and the media's `expires_at` is 7 days after Seam stores the clip. The camera needs a microSD card and Third-Party Compatibility turned on in the Tapo app. For the full flow, see [Retrieve Event Media](/docs/capability-guides/cameras/retrieve-event-media) and [Tapo event recordings](/docs/device-and-system-integration-guides/tapo-cameras#event-recordings).

The sandbox camera accepts the sandbox Tapo account password so that you can test the Connect Webview, but it has no microSD recordings, so its media would end in the `unavailable` status.

***

## 6 — Stream Live Video

You can stream a Tapo camera's live video to a browser over WebRTC when its `can_stream_live_video` capability flag is `true` and live video is turned on for your workspace. The flag means the camera supports live video and is online. It doesn't reflect whether live video is turned on for your workspace: if it isn't, [`/cameras/live_views/create`](/docs/api/cameras/live_views/create) returns a `camera_not_available` error. The flag is `false` while the camera or its Seam Bridge is offline. Live view uses a [client session token](/docs/core-concepts/authentication/client-session-tokens) scoped to the camera, so your users' browsers never see your API key. For the full flow, see [Stream Live Video](/docs/capability-guides/cameras/stream-live-video).

***

## Next Steps

Now that you've completed this guide, you can try to connect a real Tapo camera. To do so, make sure to switch to a non-sandbox workspace and API key, as real devices cannot be connected to sandbox workspaces. See the [Tapo Setup Guide](./tapo-setup-guide) for the Seam Bridge and Camera Account steps.

In addition, if you'd like to explore other aspects of Seam, here is a list of helpful resources:

* [Receiving webhooks](/docs/developer-tools/webhooks) for [device events](/docs/api/events/list)
* [Stream Live Video](/docs/capability-guides/cameras/stream-live-video)
* [Seam Bridge](/docs/capability-guides/seam-bridge)
* [Core Concepts](/docs/core-concepts/overview)

If you have any questions or want to report an issue, email us at [support@seam.co](mailto:support@seam.co).
