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

# Creating dormakaba Oracode Access Codes

> Create online and offline access codes on dormakaba Oracode locks, and learn the device-specific time constraints you must observe to create codes that are correct.

Seam supports both online and offline [access codes](/docs/low-level-apis/smart-locks/access-codes) on dormakaba Oracode locks. Owners or managers generate a code remotely, then share it with a guest through messaging or a property management system—the guest does not need to install an app to unlock the door.

<Info>
  To grant a person access, we recommend [Access Grants](/docs/use-cases/granting-access)—Seam's high-level, device-agnostic way to give someone access. An Access Grant creates the correct dormakaba Oracode access code for you and manages its lifecycle. This page covers the lower-level [Access Codes API](/docs/low-level-apis/smart-locks/access-codes) and the dormakaba Oracode-specific requirements that apply whichever API you use.
</Info>

***

## Online vs. Offline Access Codes

Each dormakaba Oracode door programs **either** online or offline access codes, never both. Seam detects each door's connectivity automatically and reports it through the device's capabilities:

* **Gateway-connected doors** program [online access codes](/docs/low-level-apis/smart-locks/access-codes) (`device.can_program_online_access_codes` is `true`).
* **Standalone doors** program [offline access codes](/docs/low-level-apis/smart-locks/access-codes/offline-access-codes) (`device.can_program_offline_access_codes` is `true`). Offline codes are enabled by a server-based registry of synchronized encryption keys (tokens) that the manufacturer maintains, so they work without the lock being online.

Check the relevant capability before you create a code. When you create an offline code, set `is_offline_access_code` to `true`; for an online code, omit it.

***

## Check Device Time Constraints

dormakaba Oracode locks do **not** accept arbitrary check-in and check-out times. Each door is configured with a fixed set of time slots (also called "user levels"), and every access code's times must match one of them. Read the device's time constraints **before** you create a code so that the times you request are valid—rather than discovering the constraint from an error or a warning.

Seam exposes these constraints in a device-agnostic form on `device.properties`:

* `device.properties.offline_time_frame_options` — for offline doors.
* `device.properties.online_time_frame_options` — for online doors.

A door defines whichever property matches its mode. Each is an array of options; pick one option and satisfy all of its rules. For the full shape of these options—`time_pairs`, `min_duration`, `max_duration`, `time_zone`, and how to choose among them—see [Understanding Time Frame Options](/docs/low-level-apis/smart-locks/access-codes/creating-access-codes/understanding-time-frame-options).

<Info>
  Read these options from the device at request time and build your time frame from the values you find—do not hardcode limits. A door's time slots can change when the installer reconfigures them.
</Info>

**Retrieve a device's time frame options:**

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const device = await seam.devices.get({
    device_id: '11111111-1111-1111-1111-444444444444',
  })

  // Read whichever property matches the door's mode.
  const timeFrameOptions = device.can_program_online_access_codes
    ? device.properties.online_time_frame_options
    : device.properties.offline_time_frame_options

  console.log(timeFrameOptions)
  ```

  ```python Python theme={"dark"}
  device = seam.devices.get(device_id="11111111-1111-1111-1111-444444444444")

  # Read whichever property matches the door's mode.
  if device.can_program_online_access_codes:
      time_frame_options = device.properties.get("online_time_frame_options")
  else:
      time_frame_options = device.properties.get("offline_time_frame_options")

  print(time_frame_options)
  ```

  ```bash cURL theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/devices/get' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "device_id": "11111111-1111-1111-1111-444444444444"
    }' | jq '.device.properties.offline_time_frame_options, .device.properties.online_time_frame_options'
  ```
</CodeGroup>

**Example `offline_time_frame_options`:**

```json theme={"dark"}
[
  {
    "display_name": "Fixed start times",
    "max_duration": "P31D",
    "time_zone": "America/Los_Angeles",
    "time_pairs": [
      {
        "display_name": "Guest RCI D 7am-7pm",
        "start_time": "07:00",
        "end_time": "19:00"
      }
    ]
  }
]
```

To create a valid code, request a duration within `max_duration` and set your check-in and check-out to one of the `time_pairs`, interpreted in the option's `time_zone`.

### Brand-Specific Time Slot Data

The raw dormakaba Oracode user levels are also available on `device.properties.dormakaba_oracode_metadata.predefined_time_slots`, along with the lock's `iana_timezone`. The device-agnostic `time_frame_options` above are derived from this data and are the recommended source of truth; the raw slots are useful when you need dormakaba-specific fields such as `is_biweekly_mode` or the user-level prefix.

```json theme={"dark"}
"predefined_time_slots": [
  {
    "name": "Guest RCI D 7am-7pm",
    "prefix": 0,
    "is_master": false,
    "is_24_hour": false,
    "is_one_shot": false,
    "check_in_time": "07:00:00[America/Los_Angeles]",
    "check_out_time": "19:00:00[America/Los_Angeles]",
    "is_biweekly_mode": false,
    "dormakaba_oracode_user_level_id": "f23721ec-6dce-4c54-8971-40d58449a366",
    "ext_dormakaba_oracode_user_level_prefix": 0
  }
]
```

### When a Request Violates the Constraints

If the time frame you request does not fit any of the door's time slots, Seam tells you in a way that depends on which API you use:

* **Access Grants:** the grant is created with a [`device_time_constraints_violated`](/docs/api/access_grants/object) warning. Its `reason` is one of `duration_exceeds_max`, `times_do_not_match_slots`, or `ongoing_not_supported`. Inspect the grant's `warnings` and adjust the times.
* **Access Codes API:** the create request fails with an error indicating that the times do not match an available time slot.

In both cases, the fix is the same: bring the requested times within one hour of a configured slot, or ask your dormakaba installer to add a slot that matches your booking pattern.

***

## dormakaba Oracode Requirements

Note the following dormakaba Oracode-specific restrictions, which apply to both Access Grants and the Access Codes API:

* **You cannot specify the PIN.** dormakaba Oracode auto-generates a six-digit code (the `cannot_specify_pin_code` [code constraint](/docs/low-level-apis/smart-locks/access-codes)), which Seam returns. Passing a `code` argument returns an error.
* **Duration limit.** An offline access code can be valid for a maximum of 31 consecutive days.
* **Codes cannot be updated or deleted.** Because offline codes cannot be removed, be mindful of device- and installation-specific code limits, especially when creating many codes at once. See your lock's user manual or contact dormakaba Oracode support for these limits.
* **Times are in the lock's local time zone.** Set `starts_at` and `ends_at` using a time and offset that match the lock's local time zone, which you specify when you [connect the site to Seam](/docs/device-and-system-integration-guides/dormakaba-oracode-locks/dormakaba-oracode-setup-guide). You can view it in `device.properties.dormakaba_oracode_metadata.iana_timezone`.
* **Master and one-time-use codes** are not currently supported.

***

## Create an Access Code

You can create hourly- and daily-bound offline access codes on standalone doors, and online access codes on gateway-connected doors. After you create a code, poll or use a webhook to confirm that it was registered successfully.

### Create an Hourly-Bound Code

Provide the `device_id`, and specify `starts_at` and `ends_at` [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) timestamps that match one of the device's time slots. For an offline door, set `is_offline_access_code` to `true`. You can also assign an optional `name`.

<Info>
  Set the `starts_at` and `ends_at` times and offsets to match the lock's local time zone.
</Info>

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  // Get the device.
  const device = await seam.locks.get({
    device_id: '11111111-1111-1111-1111-444444444444',
  })

  // Confirm that the device supports offline access codes.
  if (device.can_program_offline_access_codes) {
    // Create the hourly-bound offline access code. Make sure that the
    // validity period matches one of the device's time slots.
    await seam.accessCodes.create({
      device_id: device.device_id,
      name: 'Guest - Room 101',
      starts_at: '2024-09-10T07:00:00-07:00',
      ends_at: '2024-09-15T19:00:00-07:00',
      is_offline_access_code: true,
    })
  }
  ```

  ```python Python theme={"dark"}
  # Get the device.
  device = seam.locks.get(device_id="11111111-1111-1111-1111-444444444444")

  # Confirm that the device supports offline access codes.
  if device.can_program_offline_access_codes:
      # Create the hourly-bound offline access code. Make sure that the
      # validity period matches one of the device's time slots.
      seam.access_codes.create(
          device_id=device.device_id,
          name="Guest - Room 101",
          starts_at="2024-09-10T07:00:00-07:00",
          ends_at="2024-09-15T19:00:00-07:00",
          is_offline_access_code=True,
      )
  ```

  ```ruby Ruby theme={"dark"}
  # Get the device.
  device = seam.locks.get(device_id: "11111111-1111-1111-1111-444444444444")

  # Confirm that the device supports offline access codes.
  if device.can_program_offline_access_codes
    # Create the hourly-bound offline access code. Make sure that the
    # validity period matches one of the device's time slots.
    seam.access_codes.create(
      device_id: device.device_id,
      name: "Guest - Room 101",
      starts_at: "2024-09-10T07:00:00-07:00",
      ends_at: "2024-09-15T19:00:00-07:00",
      is_offline_access_code: true
    )
  end
  ```

  ```php PHP theme={"dark"}
  <?php
  // Get the device.
  $device = $seam->locks->get(device_id: "11111111-1111-1111-1111-444444444444");

  // Confirm that the device supports offline access codes.
  if ($device->can_program_offline_access_codes) {
    // Create the hourly-bound offline access code. Make sure that the
    // validity period matches one of the device's time slots.
    $seam->access_codes->create(
      device_id: $device->device_id,
      name: "Guest - Room 101",
      starts_at: "2024-09-10T07:00:00-07:00",
      ends_at: "2024-09-15T19:00:00-07:00",
      is_offline_access_code: true
    );
  }
  ```

  ```csharp C# theme={"dark"}
  // Get the device.
  Device device = seam.Locks.Get(deviceId: "11111111-1111-1111-1111-444444444444");

  // Confirm that the device supports offline access codes.
  if (device.CanProgramOfflineAccessCodes == true) {
    // Create the hourly-bound offline access code. Make sure that the
    // validity period matches one of the device's time slots.
    seam.AccessCodes.Create(
      deviceId: device.DeviceId,
      name: "Guest - Room 101",
      startsAt: "2024-09-10T07:00:00-07:00",
      endsAt: "2024-09-15T19:00:00-07:00",
      isOfflineAccessCode: true
    );
  }
  ```

  ```java Java theme={"dark"}
  // Get the device.
  var device = seam.locks().get(LocksGetRequest.builder()
    .deviceId("11111111-1111-1111-1111-444444444444")
    .build());

  // Confirm that the device supports offline access codes.
  if (device.getCanProgramOfflineAccessCodes().orElse(false)) {
    // Create the hourly-bound offline access code. Make sure that the
    // validity period matches one of the device's time slots.
    seam.accessCodes().create(AccessCodesCreateRequest.builder()
      .deviceId(device.getDeviceId())
      .name("Guest - Room 101")
      .startsAt("2024-09-10T07:00:00-07:00")
      .endsAt("2024-09-15T19:00:00-07:00")
      .isOfflineAccessCode(true)
      .build());
  }
  ```

  ```bash cURL theme={"dark"}
  # Get the device, then create the hourly-bound offline access code.
  # Make sure that the validity period matches one of the device's time slots.
  curl -X 'POST' \
    'https://connect.getseam.com/access_codes/create' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "device_id": "11111111-1111-1111-1111-444444444444",
      "name": "Guest - Room 101",
      "starts_at": "2024-09-10T07:00:00-07:00",
      "ends_at": "2024-09-15T19:00:00-07:00",
      "is_offline_access_code": true
    }'
  ```
</CodeGroup>

### Create a Daily-Bound Code

Daily-bound codes give you day-level granularity. Specify the **same time** (but different dates) in `starts_at` and `ends_at`. Because these codes require day-level duration granularity, set `max_time_rounding` to `1day` (or `1d`) so that Seam can round to a full day to match an available slot. Seam returns an error if `max_time_rounding` is `1hour` but the necessary rounding exceeds one hour.

The request is otherwise identical to the hourly-bound example above. The changed parameters:

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  await seam.accessCodes.create({
    device_id: device.device_id,
    name: 'Guest - Room 101',
    starts_at: '2024-09-16T00:00:00-07:00',
    ends_at: '2024-09-18T23:59:00-07:00',
    max_time_rounding: '1d',
    is_offline_access_code: true,
  })
  ```

  ```python Python theme={"dark"}
  seam.access_codes.create(
      device_id=device.device_id,
      name="Guest - Room 101",
      starts_at="2024-09-16T00:00:00-07:00",
      ends_at="2024-09-18T23:59:00-07:00",
      max_time_rounding="1d",
      is_offline_access_code=True,
  )
  ```

  ```bash cURL theme={"dark"}
  curl -X 'POST' \
    'https://connect.getseam.com/access_codes/create' \
    -H "Authorization: Bearer ${SEAM_API_KEY}" \
    -H 'Content-Type: application/json' \
    -d '{
      "device_id": "11111111-1111-1111-1111-444444444444",
      "name": "Guest - Room 101",
      "starts_at": "2024-09-16T00:00:00-07:00",
      "ends_at": "2024-09-18T23:59:00-07:00",
      "max_time_rounding": "1d",
      "is_offline_access_code": true
    }'
  ```
</CodeGroup>

### Verify That the Code Is Set

A time-bound code moves through distinct [lifecycle](/docs/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes) phases:

1. `unset` — Created on Seam but not yet on the lock, because its activation time is in the future.
2. `setting` — As `starts_at` approaches, Seam registers the code with the dormakaba Oracode server.
3. `set` — The code is programmed and ready to grant access.

Confirm registration in one of two ways:

* **Polling** — Query the access code until its `status` updates. See [Polling Method](/docs/low-level-apis/smart-locks/access-codes/creating-access-codes#polling-method).
* **Webhook** — Subscribe to the `access_code.set_on_device` and `access_code.scheduled_on_device` events. See [Webhook Events Method](/docs/low-level-apis/smart-locks/access-codes/creating-access-codes#webhook-events-method).

***

## Troubleshooting

**"No time slots found" / times do not match a slot.** Your requested times are not within one hour of any configured slot. Retrieve the device's [time frame options](#check-device-time-constraints) to see what is available, then either adjust your times or ask your dormakaba installer to add a matching slot.

**Behavior differs across doors.** Time slots are configured per door, so retrieve the time frame options for each device individually rather than assuming they are the same.

**Code creation fails for automated bookings.** Confirm that time slots are configured for the door, that your booking times fall within one hour of a slot, and that the device supports the code type you are creating (`can_program_offline_access_codes` or `can_program_online_access_codes`).

For time slot configuration help, contact your dormakaba installer. For Seam API questions, email [support@getseam.com](mailto:support@getseam.com).
