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

# Supported Device Table

> Embed Seam's live table of supported devices on your own website, filtered by the capabilities and manufacturers you care about.

export const DeviceList = ({manufacturers, capabilityName, token = "6c51f0a4-d421-429e-8295-d02271aa4f23"}) => {
  const cdn = "https://cdn.devicedb.seam.co/v/0.0.15/DeviceListByCapability.global.js";
  const close = "</scr" + "ipt>";
  const srcDoc = ['<!doctype html><html><head><meta charset="utf-8">', "<style>html,body{margin:0;padding:0;background:transparent}</style></head><body>", `<script type="module" src="${cdn}">${close}`, `<device-list-by-capability manufacturers="${manufacturers}" capability-name="${capabilityName}" token="${token}"></device-list-by-capability>`, `<script>var _h=0;function _post(){var h=document.documentElement.scrollHeight;if(h&&h!==_h){_h=h;parent.postMessage({__devicedb:true,height:h},"*")}}setInterval(_post,300);addEventListener("load",_post);_post();${close}`, "</body></html>"].join("");
  const onRef = iframe => {
    if (!iframe || iframe.dataset.devicedbBound) return;
    iframe.dataset.devicedbBound = "1";
    window.addEventListener("message", event => {
      if (event.source === iframe.contentWindow && event.data && event.data.__devicedb) {
        iframe.style.height = `${event.data.height}px`;
      }
    });
  };
  return <iframe ref={onRef} srcDoc={srcDoc} title="Compatible devices" style={{
    width: "100%",
    minHeight: "320px",
    border: "none"
  }} />;
};

The Supported Device Table is an embeddable web component that renders Seam's device database on your own site. Your customers can search it, filter it by capability, and see exactly which of their locks, thermostats, or access control systems work with your product—without you maintaining a compatibility list by hand.

The table reads from the same database that powers Seam's own integration guides, so it stays current as Seam adds device support.

Here it is embedded in this page, restricted to three lock manufacturers:

<DeviceList manufacturers="schlage,yale,august" capabilityName="can_remotely_unlock,can_program_online_access_codes" />

## Before You Begin

To embed the table, you need an **embed token** from Seam.

Email [support@getseam.com](mailto:support@getseam.com) and include every domain you plan to embed the table on. Seam creates the token for you and replies with it. This is a manual step on Seam's side, so request your token before you need it.

<Info>
  Send **full origins**, including the scheme and any non-default port—not bare hostnames. Seam matches them against the browser's `Origin` header, so `example.com` matches nothing while `https://example.com` matches. Include every environment you'll embed on:

  ```
  https://www.example.com
  https://staging.example.com
  https://*.preview.example.com
  http://localhost:3000
  ```

  Wildcards are supported per path segment, so `https://*.preview.example.com` covers your preview subdomains. Ask for a `localhost` origin at the port your dev server uses if you want the table working while you build.
</Info>

## Add the Table to Your Site

<Steps>
  <Step title="Load the component">
    Add the script tag to your page. `0.0.16` is the current release—see [Pin a Version](#pin-a-version) for how versioning works.

    ```html theme={null}
    <script
      type="module"
      src="https://cdn.devicedb.seam.co/v/0.0.16/DeviceListByCapability.global.js"
    ></script>
    ```
  </Step>

  <Step title="Place the element">
    Add the `device-list-by-capability` element wherever you want the table to appear, and set `capability-name` to the capability your customers care about.

    ```html theme={null}
    <device-list-by-capability
      capability-name="can_remotely_unlock"
      token="your-embed-token"
    ></device-list-by-capability>
    ```
  </Step>

  <Step title="Confirm it loads">
    Open the page on one of the domains you registered with Seam. You should see devices grouped by manufacturer with a search box above them.

    If you see nothing or an error, see [Troubleshooting](#troubleshooting).
  </Step>
</Steps>

The component renders inside a [shadow root](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) and brings its own styles, so it cannot inherit from or interfere with the CSS on the rest of your page.

## How the Embed Token Works

An embed token is **not** a Seam API key and carries no account access. It identifies your embed, and it only works on the domains you registered.

Seam matches the browser's `Origin` header against the origins registered for your token:

* A request from a registered origin succeeds.
* A request from any other origin returns `403 Origin not allowed for this token`.
* A request with no `Origin` header at all also returns `403`.

Matching is on the whole origin string, scheme included. `https://example.com` and `http://example.com` are different origins, and so is the same host on a different port.

Because of this, the token is safe to include in your page's HTML, where anyone can read it. Someone who copies it cannot use it on their own site.

<Warning>
  Because a missing `Origin` is rejected too, the token will not work from `curl`, from a page opened over `file://`, or from a native app shell that sends no `Origin` header. The component expects a page served over HTTP(S) from a domain you have registered.
</Warning>

To add or remove domains later, email [support@getseam.com](mailto:support@getseam.com).

## How Support Is Reported

Each device row carries a support badge for the selected capability, rather than a plain yes-or-no:

| Badge             | Meaning                                                                                                                                                     |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Live**          | The device supports the capability outright.                                                                                                                |
| **Conditionally** | Supported, but with requirements—for example, a compatible [Seam Bridge](/capability-guides/seam-bridge) may be needed. Expand the row to see what applies. |
| **No**            | The device does not support the capability.                                                                                                                 |
| **Unknown**       | Seam has no compatibility data for this device and capability yet.                                                                                          |

A device is reported as **Conditionally** whenever caveats exist, even if the underlying record says the capability is supported. The same applies when a device matches the capability more than once and those matches disagree—the table reports the more cautious answer rather than the optimistic one.

This is the main reason to embed the table instead of publishing a static list: your customers see the caveat attached to the specific model they own, and it updates as Seam's compatibility data changes.

Use `initial-supported-filter="true"` to open the table with only supported devices shown.

## Attributes

Seam can also preset any of these on your token, so the table renders the way you want without you repeating attributes on every page. Attributes you set on the element always win over the presets, so you can override a default on a single page. Ask support if you'd like defaults configured.

| Attribute                  | Type    | Default                    | Description                                                                                                                                                  |
| -------------------------- | ------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `capability-name`          | string  | —                          | **Required.** One or more [capabilities](#capabilities), comma-separated. Supplying more than one adds a dropdown so your customers can switch between them. |
| `token`                    | string  | —                          | **Required.** Your embed token.                                                                                                                              |
| `manufacturers`            | string  | —                          | Comma-separated manufacturer slugs. When set, only these manufacturers appear.                                                                               |
| `excluded-manufacturers`   | string  | —                          | Comma-separated manufacturer slugs to leave out.                                                                                                             |
| `initial-capability`       | string  | first in `capability-name` | Which capability is selected when the table loads.                                                                                                           |
| `initial-supported-filter` | boolean | `false`                    | When `true`, the table opens with the "supported only" filter already applied.                                                                               |
| `unsupported-systems`      | string  | —                          | Comma-separated slugs to display as unsupported rather than omit. Use this to be explicit about systems you do not support.                                  |
| `max-visible-rows`         | number  | `4`                        | How many device rows to show per manufacturer before a "show more" control appears.                                                                          |
| `hide-search`              | boolean | `false`                    | Hides the search input.                                                                                                                                      |
| `locale`                   | string  | `en`                       | Display language: `en`, `fr`, or `pt`.                                                                                                                       |

## Capabilities

Set `capability-name` to one or more of the following. These are the same capability flags the [Seam API](/capability-guides/device-and-system-capabilities) reports on a device, so a capability you filter by here is one you can check in code later.

| Capability                               | Shown in the table as |
| ---------------------------------------- | --------------------- |
| `can_remotely_unlock`                    | Remote Unlock         |
| `can_remotely_lock`                      | Remote Lock           |
| `can_program_online_access_codes`        | Online Access Codes   |
| `can_program_offline_access_codes`       | Offline Codes         |
| `can_provision_mobile_keys`              | Mobile Keys           |
| `can_provision_wallet_keys`              | Wallet Keys           |
| `can_encode_plastic_cards`               | Plastic Cards         |
| `can_hvac_heat`                          | HVAC Heat             |
| `can_hvac_cool`                          | HVAC Cool             |
| `can_hvac_fan`                           | HVAC Fan              |
| `can_hvac_auto`                          | HVAC Auto             |
| `can_program_thermostat_weekly_programs` | Weekly Program        |
| `can_monitor`                            | Monitoring            |

<Warning>
  Use the full `can_*` flag names exactly as written above. Shorthand names such as `remoteunlock` are not recognized, and the table renders empty when it receives one.
</Warning>

## Examples

### Offer several capabilities

Pass multiple capabilities to give your customers a dropdown, and use `initial-capability` to choose which one loads first.

```html theme={null}
<device-list-by-capability
  capability-name="can_remotely_unlock,can_program_online_access_codes,can_provision_mobile_keys"
  initial-capability="can_program_online_access_codes"
  token="your-embed-token"
></device-list-by-capability>
```

### Show only the manufacturers you integrate with

```html theme={null}
<device-list-by-capability
  capability-name="can_remotely_unlock"
  manufacturers="schlage,yale,august"
  token="your-embed-token"
></device-list-by-capability>
```

### Exclude specific manufacturers

Useful when you want the full catalog minus a few brands you do not resell.

```html theme={null}
<device-list-by-capability
  capability-name="can_remotely_unlock"
  excluded-manufacturers="ttlock"
  token="your-embed-token"
></device-list-by-capability>
```

### Build a compact thermostat table

Hide the search box, show more rows per manufacturer, and open with only supported devices visible.

```html theme={null}
<device-list-by-capability
  capability-name="can_hvac_heat,can_hvac_cool"
  max-visible-rows="10"
  hide-search="true"
  initial-supported-filter="true"
  token="your-embed-token"
></device-list-by-capability>
```

### Render the table in French

```html theme={null}
<device-list-by-capability
  capability-name="can_remotely_unlock"
  locale="fr"
  token="your-embed-token"
></device-list-by-capability>
```

`en`, `fr`, and `pt` are available. Seam can also set a default locale on your token, so the table renders in your language without the attribute.

## Dark Mode

The table follows a `dark` class on any ancestor element rather than the browser's color-scheme preference. This lets it match whatever your site's own theme toggle does.

```html theme={null}
<div class="dark">
  <device-list-by-capability
    capability-name="can_remotely_unlock"
    token="your-embed-token"
  ></device-list-by-capability>
</div>
```

If your theme switcher toggles a class on `<html>` or `<body>`, the table picks up the change automatically—no extra wiring required.

## Pin a Version

Versioned CDN URLs are immutable: the contents of `/v/0.0.16/` never change once published. Pin an explicit version in production so a new release cannot alter your page unexpectedly.

```html theme={null}
<!-- Recommended: pinned -->
<script
  type="module"
  src="https://cdn.devicedb.seam.co/v/0.0.16/DeviceListByCapability.global.js"
></script>
```

`https://cdn.devicedb.seam.co/v/latest/DeviceListByCapability.global.js` also works and always serves the newest release. It is convenient for a quick trial, but avoid it in production.

To upgrade, change the version in the URL. To roll back, point at the previous version.

## Framework Notes

The table is a standard [custom element](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements), so plain HTML, Vue, Svelte, and Angular render it directly.

**React needs one extra step.** React does not manage custom elements the way it manages its own components, and a component that mutates the DOM during hydration can throw a hydration error. Rendering the table inside an `<iframe>` sidesteps this by giving it its own document, fully outside React's tree. The `postMessage` listener keeps the frame's height matched to its content, so the table reads as an inline part of the page rather than a fixed-height box with its own scrollbar.

<Warning>
  Report the height from a timer, not from a `ResizeObserver`. A `srcdoc` iframe that the browser treats as non-rendered has its animation loop throttled, so `ResizeObserver` and `requestAnimationFrame` never fire—but timers and `load` still do. Reading `scrollHeight` inside the timer forces a reflow, so the value stays correct even while the render loop is idle, and it re-syncs when the table rewraps on window resize.
</Warning>

```jsx theme={null}
export const SupportedDeviceTable = ({ capabilityName, manufacturers, token }) => {
  const cdn =
    'https://cdn.devicedb.seam.co/v/0.0.16/DeviceListByCapability.global.js'
  // Split so the literal never terminates a surrounding <script> block if this
  // component is ever server-rendered inline into a page.
  const close = '</scr' + 'ipt>'

  const srcDoc = [
    '<!doctype html><html><head><meta charset="utf-8">',
    '<style>html,body{margin:0;padding:0;background:transparent}</style></head><body>',
    `<script type="module" src="${cdn}">${close}`,
    `<device-list-by-capability manufacturers="${manufacturers}" capability-name="${capabilityName}" token="${token}"></device-list-by-capability>`,
    `<script>var _h=0;function _post(){var h=document.documentElement.scrollHeight;if(h&&h!==_h){_h=h;parent.postMessage({__devicedb:true,height:h},"*")}}setInterval(_post,300);addEventListener("load",_post);_post();${close}`,
    '</body></html>',
  ].join('')

  const onRef = (iframe) => {
    if (!iframe || iframe.dataset.devicedbBound) return
    iframe.dataset.devicedbBound = '1'
    window.addEventListener('message', (event) => {
      if (
        event.source === iframe.contentWindow &&
        event.data &&
        event.data.__devicedb
      ) {
        iframe.style.height = `${event.data.height}px`
      }
    })
  }

  return (
    <iframe
      ref={onRef}
      srcDoc={srcDoc}
      title='Compatible devices'
      style={{ width: '100%', minHeight: '320px', border: 'none' }}
    />
  )
}
```

This is the same approach Seam uses to embed the table throughout these docs, including the example at the top of this page.

<Info>
  The iframe inherits your page's origin, so your registered domains keep working without any additional configuration.
</Info>

## Troubleshooting

<AccordionGroup>
  <Accordion title="The table is empty, with no error">
    Check your `capability-name` values against the [capability list](#capabilities). The attribute must use the full `can_*` flag—`can_remotely_unlock`, not `remoteunlock`. An unrecognized capability matches no devices and renders an empty table.

    If you also set `manufacturers`, confirm the slugs are correct. A misspelled slug filters everything out.
  </Accordion>

  <Accordion title="Requests fail with 403 Origin not allowed for this token">
    The domain serving the page is not on your token's list. Email [support@getseam.com](mailto:support@getseam.com) with the domain to have it added.

    This also appears when the page sends no `Origin` header—when opened over `file://`, for example. Serve the page over HTTP(S) from a registered domain.
  </Accordion>

  <Accordion title="Requests fail with 401 Invalid token">
    The token was not recognized. Check for a truncated or misquoted value in your HTML, and confirm you are using the embed token Seam sent you rather than a Seam API key.
  </Accordion>

  <Accordion title="Nothing renders at all">
    Confirm the script tag includes `type="module"` and that the CDN URL resolves. Custom elements upgrade once their definition loads, so a blocked or misspelled script URL leaves the element on the page with nothing inside it.
  </Accordion>

  <Accordion title="A device is missing or its support status looks wrong">
    The table reflects Seam's device database. To report a missing device or an incorrect status, email [support@getseam.com](mailto:support@getseam.com) with the manufacturer and model.
  </Accordion>
</AccordionGroup>

## Next Steps

* Browse Seam's own [device and system integration guides](/device-and-system-integration-guides/august-locks) to see the table in context.
* Check capabilities programmatically with [Device and System Capabilities](/capability-guides/device-and-system-capabilities).
* Build device management UI into your app with [Seam Components](/ui-components/overview/index).

If you have questions or want to report an issue, email [support@getseam.com](mailto:support@getseam.com).
