> ## 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.

# Ultraloq Setup Guide

> Step-by-step instructions for connecting Ultraloq devices to Seam

This guide walks you through connecting Ultraloq locks to Seam and configuring them for use with the Seam API.

## Before You Begin

To follow this guide, you need:

* A Seam account (create one at [console.seam.co](https://console.seam.co))
* An API key from your Seam workspace
* An Ultraloq account with at least one lock configured

<Info>
  If you're testing the integration, you can use a [sandbox
  workspace](/core-concepts/workspaces#sandbox-workspaces)
  with test Ultraloq devices.
</Info>

***

## Step 1: Create a Connect Webview

Create a [Connect Webview](/core-concepts/connect-webviews) to enable the Ultraloq device owner to authorize Seam to access their Ultraloq account.

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

  const seam = new Seam()

  const webview = await seam.connectWebviews.create({
    accepted_providers: ['ultraloq'],
    custom_redirect_url: 'https://your-app.com/oauth/callback',
  })

  console.log(webview.url)
  ```

  ```bash cURL theme={null}
  curl -X 'POST' \
    'https://connect.getseam.com/connect_webviews/create' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "accepted_providers": ["ultraloq"],
      "custom_redirect_url": "https://your-app.com/oauth/callback"
    }'
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  webview = seam.connect_webviews.create(
    accepted_providers=["ultraloq"],
    custom_redirect_url="https://your-app.com/oauth/callback"
  )

  print(webview.url)
  ```

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

  seam = Seam.new()

  webview = seam.connect_webviews.create(
    accepted_providers: ["ultraloq"],
    custom_redirect_url: "https://your-app.com/oauth/callback"
  )

  puts webview.url
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  $webview = $seam->connect_webviews->create(
    accepted_providers: ["ultraloq"],
    custom_redirect_url: "https://your-app.com/oauth/callback"
  );

  echo $webview->url;
  ```

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

  var seam = new SeamClient();

  var webview = seam.ConnectWebviews.Create(
    acceptedProviders: new[] { "ultraloq" },
    customRedirectUrl: "https://your-app.com/oauth/callback"
  );

  Console.WriteLine(webview.Url);
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.ConnectWebview;

  Seam seam = Seam.builder().build();

  ConnectWebview webview = seam.connectWebviews().create(
    ConnectWebviewsCreateRequest.builder()
      .acceptedProviders(List.of("ultraloq"))
      .customRedirectUrl("https://your-app.com/oauth/callback")
      .build()
  );

  System.out.println(webview.getUrl());
  ```
</CodeGroup>

***

## Step 2: User Authorization

Direct the user to the Connect Webview URL returned in Step 1. The user will be prompted to:

1. Sign in to their Ultraloq account
2. Authorize Seam to access their Ultraloq devices

After authorization, the user is redirected to your `custom_redirect_url` with the `connect_webview_id` as a query parameter.

***

## Step 3: Verify Connection

Wait for the Connect Webview status to change to `authorized`, indicating that the connection was successful. You can either poll the Connect Webview or use [webhooks](/developer-tools/webhooks) to be notified when the status changes.

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

  const seam = new Seam()

  // Poll until authorized
  const webview = await seam.connectWebviews.get({
    connect_webview_id: webview.connect_webview_id,
  })

  if (webview.status === 'authorized') {
    console.log('Connection successful!')
    console.log(`Connected account ID: ${webview.connected_account_id}`)
  }
  ```

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

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  # Poll until authorized
  webview = seam.connect_webviews.get(
    connect_webview_id=webview.connect_webview_id
  )

  if webview.status == "authorized":
    print("Connection successful!")
    print(f"Connected account ID: {webview.connected_account_id}")
  ```

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

  seam = Seam.new()

  # Poll until authorized
  webview = seam.connect_webviews.get(
    connect_webview_id: webview.connect_webview_id
  )

  if webview.status == "authorized"
    puts "Connection successful!"
    puts "Connected account ID: #{webview.connected_account_id}"
  end
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  // Poll until authorized
  $webview = $seam->connect_webviews->get(
    connect_webview_id: $webview->connect_webview_id
  );

  if ($webview->status === "authorized") {
    echo "Connection successful!\n";
    echo "Connected account ID: " . $webview->connected_account_id;
  }
  ```

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

  var seam = new SeamClient();

  // Poll until authorized
  var webview = seam.ConnectWebviews.Get(
    connectWebviewId: webview.ConnectWebviewId
  );

  if (webview.Status == "authorized")
  {
    Console.WriteLine("Connection successful!");
    Console.WriteLine($"Connected account ID: {webview.ConnectedAccountId}");
  }
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.ConnectWebview;

  Seam seam = Seam.builder().build();

  // Poll until authorized
  ConnectWebview webview = seam.connectWebviews().get(
    ConnectWebviewsGetRequest.builder()
      .connectWebviewId(webview.getConnectWebviewId())
      .build()
  );

  if (webview.getStatus().equals("authorized")) {
    System.out.println("Connection successful!");
    System.out.println("Connected account ID: " + webview.getConnectedAccountId());
  }
  ```
</CodeGroup>

***

## Step 4: List Devices

Once the connection is authorized, retrieve the list of Ultraloq devices associated with the connected account.

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

  const seam = new Seam()

  const devices = await seam.devices.list({
    connected_account_id: webview.connected_account_id,
  })

  for (const device of devices) {
    console.log(`Device: ${device.properties.name}`)
    console.log(`Device ID: ${device.device_id}`)
    console.log(`Warnings:`, device.warnings)
    console.log()
  }
  ```

  ```bash cURL theme={null}
  curl -X 'POST' \
    'https://connect.getseam.com/devices/list' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"connected_account_id\": \"${CONNECTED_ACCOUNT_ID}\"
    }"
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  devices = seam.devices.list(
    connected_account_id=webview.connected_account_id
  )

  for device in devices:
    print(f"Device: {device.properties['name']}")
    print(f"Device ID: {device.device_id}")
    print(f"Warnings: {device.warnings}")
    print()
  ```

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

  seam = Seam.new()

  devices = seam.devices.list(
    connected_account_id: webview.connected_account_id
  )

  devices.each do |device|
    puts "Device: #{device.properties['name']}"
    puts "Device ID: #{device.device_id}"
    puts "Warnings: #{device.warnings}"
    puts
  end
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  $devices = $seam->devices->list(
    connected_account_id: $webview->connected_account_id
  );

  foreach ($devices as $device) {
    echo "Device: " . $device->properties->name . "\n";
    echo "Device ID: " . $device->device_id . "\n";
    echo "Warnings: " . json_encode($device->warnings) . "\n\n";
  }
  ```

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

  var seam = new SeamClient();

  var devices = seam.Devices.List(
    connectedAccountId: webview.ConnectedAccountId
  );

  foreach (var device in devices)
  {
    Console.WriteLine($"Device: {device.Properties.Name}");
    Console.WriteLine($"Device ID: {device.DeviceId}");
    Console.WriteLine($"Warnings: {string.Join(", ", device.Warnings)}");
    Console.WriteLine();
  }
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.Device;

  Seam seam = Seam.builder().build();

  List<Device> devices = seam.devices().list(
    DevicesListRequest.builder()
      .connectedAccountId(webview.getConnectedAccountId())
      .build()
  );

  for (Device device : devices) {
    System.out.println("Device: " + device.getProperties().getName());
    System.out.println("Device ID: " + device.getDeviceId());
    System.out.println("Warnings: " + device.getWarnings());
    System.out.println();
  }
  ```
</CodeGroup>

**Expected Output:**

When you first list Ultraloq devices, they will have the `ultraloq_time_zone_unknown` warning:

```json theme={null}
{
  "device_id": "11111111-2222-3333-4444-555555555555",
  "device_type": "ultraloq_lock",
  "can_program_online_access_codes": true,
  "can_remotely_lock": true,
  "can_remotely_unlock": true,
  "warnings": [
    {
      "warning_code": "ultraloq_time_zone_unknown",
      "message": "Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes."
    }
  ],
  "properties": {
    "ultraloq_metadata": {
      "time_zone": null
    }
  }
}
```

<Warning>
  **Important:** The `ultraloq_time_zone_unknown` warning indicates that you
  must configure the device's timezone before creating time-bound access codes.
  Proceed to Step 5 to configure timezones.
</Warning>

***

## Step 5: Configure Device Timezones

This is a **required step** for Ultraloq devices. You must configure each device's timezone before you can create time-bound access codes.

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

  const seam = new Seam()

  // Configure timezone for one or more devices
  await seam.devices.reportProviderMetadata({
    devices: [
      {
        device_id: device.device_id,
        ultraloq_metadata: {
          time_zone: 'America/New_York',
        },
      },
    ],
  })

  console.log('Timezone configured successfully!')
  ```

  ```bash cURL theme={null}
  curl -X 'POST' \
    'https://connect.getseam.com/devices/report_provider_metadata' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"devices\": [
        {
          \"device_id\": \"${DEVICE_ID}\",
          \"ultraloq_metadata\": {
            \"time_zone\": \"America/New_York\"
          }
        }
      ]
    }"
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  # Configure timezone for one or more devices
  seam.devices.report_provider_metadata(
    devices=[
      {
        "device_id": device.device_id,
        "ultraloq_metadata": {
          "time_zone": "America/New_York"
        }
      }
    ]
  )

  print("Timezone configured successfully!")
  ```

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

  seam = Seam.new()

  # Configure timezone for one or more devices
  seam.devices.report_provider_metadata(
    devices: [
      {
        device_id: device.device_id,
        ultraloq_metadata: {
          time_zone: "America/New_York"
        }
      }
    ]
  )

  puts "Timezone configured successfully!"
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  // Configure timezone for one or more devices
  $seam->devices->report_provider_metadata(
    devices: [
      [
        "device_id" => $device->device_id,
        "ultraloq_metadata" => [
          "time_zone" => "America/New_York"
        ]
      ]
    ]
  );

  echo "Timezone configured successfully!";
  ```

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

  var seam = new SeamClient();

  // Configure timezone for one or more devices
  seam.Devices.ReportProviderMetadata(
    devices: new[] {
      new DeviceMetadata {
        DeviceId = device.DeviceId,
        UltraloqMetadata = new UltraloqMetadata {
          TimeZone = "America/New_York"
        }
      }
    }
  );

  Console.WriteLine("Timezone configured successfully!");
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;

  Seam seam = Seam.builder().build();

  // Configure timezone for one or more devices
  seam.devices().reportProviderMetadata(
    DevicesReportProviderMetadataRequest.builder()
      .devices(List.of(
        DeviceMetadata.builder()
          .deviceId(device.getDeviceId())
          .ultraloqMetadata(UltraloqMetadata.builder()
            .timeZone("America/New_York")
            .build())
          .build()
      ))
      .build()
  );

  System.out.println("Timezone configured successfully!");
  ```
</CodeGroup>

<Check>
  **Valid Timezone Values:** Use IANA timezone strings such as `"America/New_York"`, `"Europe/London"`, or `"Asia/Tokyo"`. Do not use timezone abbreviations like `"EST"` or `"PST"`.

  For a complete list of valid timezones, see the [IANA Time Zone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
</Check>

For detailed information about timezone configuration, including best practices and troubleshooting, see [Configuring Ultraloq Device Timezones](/device-and-system-integration-guides/ultraloq-locks/configuring-ultraloq-device-timezones).

***

## Step 6: Verify Configuration

After configuring the timezone, verify that the warning has been cleared and the timezone is set correctly.

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

  const seam = new Seam()

  const device = await seam.devices.get({
    device_id: device.device_id,
  })

  // Check that timezone is configured
  console.assert(
    device.properties.ultraloq_metadata.time_zone === 'America/New_York',
    'Timezone should be set',
  )

  // Check that warning is cleared
  const hasWarning = device.warnings.some(
    (w) => w.warning_code === 'ultraloq_time_zone_unknown',
  )
  console.assert(!hasWarning, 'Timezone warning should be cleared')

  console.log('✓ Device is ready to create time-bound access codes!')
  ```

  ```bash cURL theme={null}
  device=$(curl -X 'POST' \
    'https://connect.getseam.com/devices/get' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"device_id\": \"${DEVICE_ID}\"
    }")

  # Check timezone is set
  echo $device | jq '.device.properties.ultraloq_metadata.time_zone'

  # Check warnings are cleared
  echo $device | jq '.device.warnings'
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  device = seam.devices.get(device_id=device.device_id)

  # Check that timezone is configured
  assert device.properties["ultraloq_metadata"]["time_zone"] == "America/New_York"

  # Check that warning is cleared
  has_warning = any(
    w.warning_code == "ultraloq_time_zone_unknown"
    for w in device.warnings
  )
  assert not has_warning, "Timezone warning should be cleared"

  print("✓ Device is ready to create time-bound access codes!")
  ```

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

  seam = Seam.new()

  device = seam.devices.get(device_id: device.device_id)

  # Check that timezone is configured
  raise "Timezone not set" unless device.properties["ultraloq_metadata"]["time_zone"] == "America/New_York"

  # Check that warning is cleared
  has_warning = device.warnings.any? { |w| w.warning_code == "ultraloq_time_zone_unknown" }
  raise "Timezone warning not cleared" if has_warning

  puts "✓ Device is ready to create time-bound access codes!"
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  $device = $seam->devices->get(device_id: $device->device_id);

  // Check that timezone is configured
  assert($device->properties->ultraloq_metadata->time_zone === "America/New_York");

  // Check that warning is cleared
  $hasWarning = false;
  foreach ($device->warnings as $warning) {
    if ($warning->warning_code === "ultraloq_time_zone_unknown") {
      $hasWarning = true;
      break;
    }
  }
  assert(!$hasWarning, "Timezone warning should be cleared");

  echo "✓ Device is ready to create time-bound access codes!";
  ```

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

  var seam = new SeamClient();

  var device = seam.Devices.Get(deviceId: device.DeviceId);

  // Check that timezone is configured
  Debug.Assert(
    device.Properties.UltraloqMetadata.TimeZone == "America/New_York",
    "Timezone should be set"
  );

  // Check that warning is cleared
  var hasWarning = device.Warnings.Any(
    w => w.WarningCode == "ultraloq_time_zone_unknown"
  );
  Debug.Assert(!hasWarning, "Timezone warning should be cleared");

  Console.WriteLine("✓ Device is ready to create time-bound access codes!");
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.Device;

  Seam seam = Seam.builder().build();

  Device device = seam.devices().get(
    DevicesGetRequest.builder()
      .deviceId(device.getDeviceId())
      .build()
  );

  // Check that timezone is configured
  assert device.getProperties().getUltraloqMetadata().getTimeZone().equals("America/New_York");

  // Check that warning is cleared
  boolean hasWarning = device.getWarnings().stream()
    .anyMatch(w -> w.getWarningCode().equals("ultraloq_time_zone_unknown"));
  assert !hasWarning : "Timezone warning should be cleared";

  System.out.println("✓ Device is ready to create time-bound access codes!");
  ```
</CodeGroup>

***

## Next Steps

Now that your Ultraloq devices are connected and configured, you can perform common operations:

### Lock and Unlock Devices

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

  const seam = new Seam()

  // Lock the door
  await seam.locks.lockDoor({ device_id: 'your-device-id' })
  console.log('Door locked')

  // Unlock the door
  await seam.locks.unlockDoor({ device_id: 'your-device-id' })
  console.log('Door unlocked')
  ```

  ```bash cURL theme={null}
  # Lock the door
  curl -X 'POST' \
    'https://connect.getseam.com/locks/lock_door' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{\"device_id\": \"your-device-id\"}"

  # Unlock the door
  curl -X 'POST' \
    'https://connect.getseam.com/locks/unlock_door' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{\"device_id\": \"your-device-id\"}"
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  # Lock the door
  seam.locks.lock_door(device_id="your-device-id")
  print("Door locked")

  # Unlock the door
  seam.locks.unlock_door(device_id="your-device-id")
  print("Door unlocked")
  ```

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

  seam = Seam.new()

  # Lock the door
  seam.locks.lock_door(device_id: "your-device-id")
  puts "Door locked"

  # Unlock the door
  seam.locks.unlock_door(device_id: "your-device-id")
  puts "Door unlocked"
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  // Lock the door
  $seam->locks->lock_door(device_id: "your-device-id");
  echo "Door locked\n";

  // Unlock the door
  $seam->locks->unlock_door(device_id: "your-device-id");
  echo "Door unlocked\n";
  ```

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

  var seam = new SeamClient();

  // Lock the door
  seam.Locks.LockDoor(deviceId: "your-device-id");
  Console.WriteLine("Door locked");

  // Unlock the door
  seam.Locks.UnlockDoor(deviceId: "your-device-id");
  Console.WriteLine("Door unlocked");
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;

  Seam seam = Seam.builder().build();

  // Lock the door
  seam.locks().lockDoor(LocksLockDoorRequest.builder()
    .deviceId("your-device-id")
    .build());
  System.out.println("Door locked");

  // Unlock the door
  seam.locks().unlockDoor(LocksUnlockDoorRequest.builder()
    .deviceId("your-device-id")
    .build());
  System.out.println("Door unlocked");
  ```
</CodeGroup>

### Create Permanent Access Codes

Permanent access codes work indefinitely and do not require timezone configuration:

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

  const seam = new Seam()

  // Create permanent access code
  const accessCode = await seam.accessCodes.create({
    device_id: 'your-device-id',
    name: 'Maintenance Team',
    code: '1234', // Optional: auto-generated if omitted
  })

  console.log(`Created permanent code: ${accessCode.code}`)
  ```

  ```bash cURL theme={null}
  curl -X 'POST' \
    'https://connect.getseam.com/access_codes/create' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "device_id": "your-device-id",
      "name": "Maintenance Team",
      "code": "1234"
    }'
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  # Create permanent access code
  access_code = seam.access_codes.create(
    device_id="your-device-id",
    name="Maintenance Team",
    code="1234"  # Optional: auto-generated if omitted
  )

  print(f"Created permanent code: {access_code.code}")
  ```

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

  seam = Seam.new()

  # Create permanent access code
  access_code = seam.access_codes.create(
    device_id: "your-device-id",
    name: "Maintenance Team",
    code: "1234"  # Optional: auto-generated if omitted
  )

  puts "Created permanent code: #{access_code.code}"
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  // Create permanent access code
  $accessCode = $seam->access_codes->create(
    device_id: "your-device-id",
    name: "Maintenance Team",
    code: "1234"  // Optional: auto-generated if omitted
  );

  echo "Created permanent code: " . $accessCode->code . "\n";
  ```

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

  var seam = new SeamClient();

  // Create permanent access code
  var accessCode = seam.AccessCodes.Create(
    deviceId: "your-device-id",
    name: "Maintenance Team",
    code: "1234"  // Optional: auto-generated if omitted
  );

  Console.WriteLine($"Created permanent code: {accessCode.Code}");
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.AccessCode;

  Seam seam = Seam.builder().build();

  // Create permanent access code
  AccessCode accessCode = seam.accessCodes().create(
    AccessCodesCreateRequest.builder()
      .deviceId("your-device-id")
      .name("Maintenance Team")
      .code("1234")  // Optional: auto-generated if omitted
      .build()
  );

  System.out.println("Created permanent code: " + accessCode.getCode());
  ```
</CodeGroup>

### Create Time-Bound Access Codes

Time-bound access codes require timezone configuration (completed in Step 5):

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

  const seam = new Seam()

  // Define time range
  const startsAt = new Date(Date.now() + 24 * 60 * 60 * 1000)
  const endsAt = new Date(startsAt.getTime() + 2 * 24 * 60 * 60 * 1000)

  // Create time-bound access code
  const accessCode = await seam.accessCodes.create({
    device_id: 'your-device-id',
    name: 'Weekend Guest',
    starts_at: startsAt.toISOString(),
    ends_at: endsAt.toISOString(),
  })

  console.log(`Created time-bound code: ${accessCode.code}`)
  console.log(`Active from ${accessCode.starts_at} to ${accessCode.ends_at}`)
  ```

  ```bash cURL theme={null}
  # Calculate timestamps
  STARTS_AT=$(date -u -d '+1 day' '+%Y-%m-%dT%H:%M:%SZ')
  ENDS_AT=$(date -u -d '+3 days' '+%Y-%m-%dT%H:%M:%SZ')

  curl -X 'POST' \
    'https://connect.getseam.com/access_codes/create' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"device_id\": \"your-device-id\",
      \"name\": \"Weekend Guest\",
      \"starts_at\": \"${STARTS_AT}\",
      \"ends_at\": \"${ENDS_AT}\"
    }"
  ```

  ```python Python theme={null}
  from seam import Seam
  from datetime import datetime, timedelta

  seam = Seam()

  # Define time range
  starts_at = datetime.utcnow() + timedelta(days=1)
  ends_at = starts_at + timedelta(days=2)

  # Create time-bound access code
  access_code = seam.access_codes.create(
    device_id="your-device-id",
    name="Weekend Guest",
    starts_at=starts_at.isoformat() + "Z",
    ends_at=ends_at.isoformat() + "Z"
  )

  print(f"Created time-bound code: {access_code.code}")
  print(f"Active from {access_code.starts_at} to {access_code.ends_at}")
  ```

  ```ruby Ruby theme={null}
  require "seam"
  require "time"

  seam = Seam.new()

  # Define time range
  starts_at = Time.now.utc + (24 * 60 * 60)
  ends_at = starts_at + (2 * 24 * 60 * 60)

  # Create time-bound access code
  access_code = seam.access_codes.create(
    device_id: "your-device-id",
    name: "Weekend Guest",
    starts_at: starts_at.iso8601,
    ends_at: ends_at.iso8601
  )

  puts "Created time-bound code: #{access_code.code}"
  puts "Active from #{access_code.starts_at} to #{access_code.ends_at}"
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  // Define time range
  $startsAt = new DateTime('+1 day', new DateTimeZone('UTC'));
  $endsAt = (clone $startsAt)->add(new DateInterval('P2D'));

  // Create time-bound access code
  $accessCode = $seam->access_codes->create(
    device_id: "your-device-id",
    name: "Weekend Guest",
    starts_at: $startsAt->format(DateTime::ATOM),
    ends_at: $endsAt->format(DateTime::ATOM)
  );

  echo "Created time-bound code: " . $accessCode->code . "\n";
  echo "Active from " . $accessCode->starts_at . " to " . $accessCode->ends_at . "\n";
  ```

  ```csharp C# theme={null}
  using Seam.Client;
  using System;

  var seam = new SeamClient();

  // Define time range
  var startsAt = DateTime.UtcNow.AddDays(1);
  var endsAt = startsAt.AddDays(2);

  // Create time-bound access code
  var accessCode = seam.AccessCodes.Create(
    deviceId: "your-device-id",
    name: "Weekend Guest",
    startsAt: startsAt,
    endsAt: endsAt
  );

  Console.WriteLine($"Created time-bound code: {accessCode.Code}");
  Console.WriteLine($"Active from {accessCode.StartsAt} to {accessCode.EndsAt}");
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.AccessCode;
  import java.time.Instant;
  import java.time.temporal.ChronoUnit;

  Seam seam = Seam.builder().build();

  // Define time range
  Instant startsAt = Instant.now().plus(1, ChronoUnit.DAYS);
  Instant endsAt = startsAt.plus(2, ChronoUnit.DAYS);

  // Create time-bound access code
  AccessCode accessCode = seam.accessCodes().create(
    AccessCodesCreateRequest.builder()
      .deviceId("your-device-id")
      .name("Weekend Guest")
      .startsAt(startsAt.toString())
      .endsAt(endsAt.toString())
      .build()
  );

  System.out.println("Created time-bound code: " + accessCode.getCode());
  System.out.println("Active from " + accessCode.getStartsAt() + " to " + accessCode.getEndsAt());
  ```
</CodeGroup>

### Monitor Device Status

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

  const seam = new Seam()

  const device = await seam.devices.get({ device_id: 'your-device-id' })

  // Check lock status
  console.log(`Lock status: ${device.properties.locked}`)

  // Check online status
  console.log(`Online: ${device.properties.online}`)

  // Check battery level (if available)
  if (device.properties.battery_level) {
    console.log(`Battery level: ${device.properties.battery_level}`)
  }

  // Check for warnings
  if (device.warnings.length > 0) {
    console.log(`Warnings: ${device.warnings.map((w) => w.warning_code)}`)
  }
  ```

  ```bash cURL theme={null}
  device=$(curl -X 'POST' \
    'https://connect.getseam.com/devices/get' \
    -H 'accept: application/json' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d "{\"device_id\": \"your-device-id\"}")

  # Check lock status
  echo $device | jq '.device.properties.locked'

  # Check online status
  echo $device | jq '.device.properties.online'

  # Check battery level
  echo $device | jq '.device.properties.battery_level'

  # Check warnings
  echo $device | jq '.device.warnings'
  ```

  ```python Python theme={null}
  from seam import Seam

  seam = Seam()

  device = seam.devices.get(device_id="your-device-id")

  # Check lock status
  print(f"Lock status: {device.properties['locked']}")

  # Check online status
  print(f"Online: {device.properties['online']}")

  # Check battery level (if available)
  if 'battery_level' in device.properties:
    print(f"Battery level: {device.properties['battery_level']}")

  # Check for warnings
  if device.warnings:
    print(f"Warnings: {[w.warning_code for w in device.warnings]}")
  ```

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

  seam = Seam.new()

  device = seam.devices.get(device_id: "your-device-id")

  # Check lock status
  puts "Lock status: #{device.properties['locked']}"

  # Check online status
  puts "Online: #{device.properties['online']}"

  # Check battery level (if available)
  if device.properties['battery_level']
    puts "Battery level: #{device.properties['battery_level']}"
  end

  # Check for warnings
  if device.warnings.any?
    puts "Warnings: #{device.warnings.map(&:warning_code)}"
  end
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Seam\SeamClient;

  $seam = new SeamClient();

  $device = $seam->devices->get(device_id: "your-device-id");

  // Check lock status
  echo "Lock status: " . ($device->properties->locked ? "locked" : "unlocked") . "\n";

  // Check online status
  echo "Online: " . ($device->properties->online ? "yes" : "no") . "\n";

  // Check battery level (if available)
  if (isset($device->properties->battery_level)) {
    echo "Battery level: " . $device->properties->battery_level . "\n";
  }

  // Check for warnings
  if (count($device->warnings) > 0) {
    $warningCodes = array_map(fn($w) => $w->warning_code, $device->warnings);
    echo "Warnings: " . implode(", ", $warningCodes) . "\n";
  }
  ```

  ```csharp C# theme={null}
  using Seam.Client;
  using System.Linq;

  var seam = new SeamClient();

  var device = seam.Devices.Get(deviceId: "your-device-id");

  // Check lock status
  Console.WriteLine($"Lock status: {device.Properties.Locked}");

  // Check online status
  Console.WriteLine($"Online: {device.Properties.Online}");

  // Check battery level (if available)
  if (device.Properties.BatteryLevel.HasValue)
  {
    Console.WriteLine($"Battery level: {device.Properties.BatteryLevel}");
  }

  // Check for warnings
  if (device.Warnings.Any())
  {
    var warningCodes = string.Join(", ", device.Warnings.Select(w => w.WarningCode));
    Console.WriteLine($"Warnings: {warningCodes}");
  }
  ```

  ```java Java theme={null}
  import com.seam.api.Seam;
  import com.seam.api.types.Device;
  import java.util.stream.Collectors;

  Seam seam = Seam.builder().build();

  Device device = seam.devices().get(
    DevicesGetRequest.builder()
      .deviceId("your-device-id")
      .build()
  );

  // Check lock status
  System.out.println("Lock status: " + device.getProperties().getLocked());

  // Check online status
  System.out.println("Online: " + device.getProperties().getOnline());

  // Check battery level (if available)
  if (device.getProperties().getBatteryLevel() != null) {
    System.out.println("Battery level: " + device.getProperties().getBatteryLevel());
  }

  // Check for warnings
  if (!device.getWarnings().isEmpty()) {
    String warningCodes = device.getWarnings().stream()
      .map(w -> w.getWarningCode())
      .collect(Collectors.joining(", "));
    System.out.println("Warnings: " + warningCodes);
  }
  ```
</CodeGroup>

***

## Related Resources

* **[Creating Ultraloq Access Codes](/device-and-system-integration-guides/ultraloq-locks/creating-ultraloq-access-codes)** - Detailed guide on permanent and time-bound access codes
* **[Configuring Ultraloq Device Timezones](/device-and-system-integration-guides/ultraloq-locks/configuring-ultraloq-device-timezones)** - Complete timezone configuration reference
* **[Lock and Unlock Operations](/low-level-apis/smart-locks/lock-and-unlock)** - General smart lock control documentation
* **[Access Codes Overview](/low-level-apis/smart-locks/access-codes)** - Understanding access code management

***

## Troubleshooting

### Devices not appearing after connection

If devices don't appear after connecting your Ultraloq account:

1. Verify that your locks are connected to Wi-Fi in the Ultraloq mobile app
2. Ensure your Ultraloq account has access to the locks you're trying to connect
3. Wait a few minutes for the initial sync to complete

### Warning persists after setting timezone

If the `ultraloq_time_zone_unknown` warning persists after setting the timezone:

1. Verify you used a valid IANA timezone string (e.g., `"America/New_York"`, not `"EST"`)
2. Check that the API call succeeded without errors
3. Refresh the device by calling `seam.devices.get()` to get the latest state

### Time-bound access codes fail to create

If you receive an error when creating time-bound access codes:

1. Confirm the device's timezone is configured (check `device.properties.ultraloq_metadata.time_zone`)
2. Verify the `ultraloq_time_zone_unknown` warning is not present in `device.warnings`
3. Ensure you're providing both `starts_at` and `ends_at` parameters

***
