seam-access-codes

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Seam Access Codes (Lower-level API)

Seam Access Codes(低级API)

You are an expert Seam integration engineer. Write the integration code directly into the developer's existing codebase.
您是一位资深的Seam集成工程师,请将集成代码直接写入开发者的现有代码库中。

Approach

方法

  1. Move fast. Glob for key files (booking/reservation handlers, routes, models), read them, start writing code.
  2. Write code in existing files. Add Seam calls directly into existing service/handler functions. Don't create wrapper services.
  3. Minimize changes. Only touch files that need Seam calls + webhook route. Install SDK, add import, add calls.
  1. 快速推进:查找关键文件(预订/预约处理器、路由、模型),读取文件后开始编写代码。
  2. 在现有文件中编写代码:将Seam调用直接添加到现有服务/处理器函数中,不要创建包装服务。
  3. 最小化修改:仅修改需要添加Seam调用的文件以及webhook路由。安装SDK、添加导入语句、添加调用。

How Access Codes works

Access Codes的工作原理

You create time-bound access codes directly on specific devices. Each code has a
starts_at
and
ends_at
window. You must store the
access_code_id
to update or delete it later. Seam programs the code onto the lock and removes it when it expires or you delete it.
您可以直接在特定设备上创建限时访问码。每个访问码都有
starts_at
ends_at
时间窗口。您必须存储
access_code_id
,以便后续更新或删除该访问码。Seam会将访问码写入锁中,并在过期或您删除时将其移除。

1. Install SDK + initialize

1. 安装SDK并初始化

Do NOT pin to a specific version.
bash
npm install seam        # Node.js
pip install seam        # Python
bundle add seam         # Ruby
CRITICAL for Next.js:
new Seam()
at module scope BREAKS
next build
. Use a lazy getter:
typescript
import { Seam } from "seam";
let _seam: Seam;
function getSeam() {
  if (!_seam) _seam = new Seam({ apiKey: process.env.SEAM_API_KEY! });
  return _seam;
}
For Express / standard Node.js:
typescript
import { Seam } from "seam";
const seam = new Seam({ apiKey: process.env.SEAM_API_KEY });
python
from seam import Seam
seam = Seam(api_key=os.environ["SEAM_API_KEY"])
请勿固定到特定版本。
bash
npm install seam        # Node.js
pip install seam        # Python
bundle add seam         # Ruby
Next.js 关键注意事项:在模块作用域使用
new Seam()
会导致
next build
失败,请使用延迟 getter:
typescript
import { Seam } from "seam";
let _seam: Seam;
function getSeam() {
  if (!_seam) _seam = new Seam({ apiKey: process.env.SEAM_API_KEY! });
  return _seam;
}
对于Express / 标准Node.js:
typescript
import { Seam } from "seam";
const seam = new Seam({ apiKey: process.env.SEAM_API_KEY });
python
from seam import Seam
seam = Seam(api_key=os.environ["SEAM_API_KEY"])

2. Get the device ID

2. 获取设备ID

Access Codes targets a specific device by
device_id
. Each room/door must map to its own device ID — never use a single global device for all rooms.
Look for device IDs in:
  • Environment variables per room:
    SEAM_DEVICE_ROOM_A1
    ,
    SEAM_DEVICE_ID_ROOM_101
    , etc.
  • The app's data model (e.g.,
    room.seamDeviceId
    ,
    unit.deviceId
    )
If the mapping is missing, fail the operation — do not fall back to a global device.
Access Codes通过
device_id
定位特定设备。每个房间/门必须映射到其自身的设备ID——切勿为所有房间使用单个全局设备。
可在以下位置查找设备ID:
  • 每个房间的环境变量:
    SEAM_DEVICE_ROOM_A1
    SEAM_DEVICE_ID_ROOM_101
    等。
  • 应用的数据模型(例如:
    room.seamDeviceId
    unit.deviceId
如果缺少映射,请终止操作——不要回退到全局设备。

3. Create access code on booking creation

3. 创建预订时生成访问码

Add directly inside the create function. Store the
access_code_id
on the booking object.
typescript
// Inside createBooking(), after saving the booking:
const deviceId = getDeviceIdForRoom(room);  // Must resolve per-room
if (!deviceId) {
  throw new Error(`No Seam device configured for room ${room.id}`);
}
try {
  const accessCode = await seam.accessCodes.create({
    device_id: deviceId,
    name: `${member.name} - ${room.name}`,
    starts_at: booking.startTime,
    ends_at: booking.endTime
  });
  booking.seamAccessCodeId = accessCode.access_code_id;
  booking.accessCode = accessCode.code;  // The actual PIN
} catch (err) {
  console.error("Seam access code creation failed:", err);
  // Consider: should this fail the booking? If access is required, throw.
}
python
undefined
直接在创建函数内添加代码。
access_code_id
存储在预订对象中。
typescript
// Inside createBooking(), after saving the booking:
const deviceId = getDeviceIdForRoom(room);  // Must resolve per-room
if (!deviceId) {
  throw new Error(`No Seam device configured for room ${room.id}`);
}
try {
  const accessCode = await seam.accessCodes.create({
    device_id: deviceId,
    name: `${member.name} - ${room.name}`,
    starts_at: booking.startTime,
    ends_at: booking.endTime
  });
  booking.seamAccessCodeId = accessCode.access_code_id;
  booking.accessCode = accessCode.code;  // The actual PIN
} catch (err) {
  console.error("Seam access code creation failed:", err);
  // Consider: should this fail the booking? If access is required, throw.
}
python
undefined

Inside create_booking(), after saving:

Inside create_booking(), after saving:

device_id = get_device_id_for_room(room) # Must resolve per-room if not device_id: raise ValueError(f"No Seam device configured for room {room.id}") try: access_code = seam.access_codes.create( device_id=device_id, name=f"{member.name} - {room.name}", starts_at=booking.start_time, ends_at=booking.end_time ) booking.seam_access_code_id = access_code.access_code_id booking.access_code = access_code.code except Exception as e: print(f"Seam access code failed: {e}") # Consider: should this fail the booking? If access is required, raise.
undefined
device_id = get_device_id_for_room(room) # Must resolve per-room if not device_id: raise ValueError(f"No Seam device configured for room {room.id}") try: access_code = seam.access_codes.create( device_id=device_id, name=f"{member.name} - {room.name}", starts_at=booking.start_time, ends_at=booking.end_time ) booking.seam_access_code_id = access_code.access_code_id booking.access_code = access_code.code except Exception as e: print(f"Seam access code failed: {e}") # Consider: should this fail the booking? If access is required, raise.
undefined

Gotchas

注意事项

  • Store
    access_code_id
    — you need it for update and delete. Add a field to the booking model if one doesn't exist. If it's
    null
    /
    undefined
    , the code failed and needs retry.
  • Never use a global device fallback — each room must map to its specific device.
  • device_id
    is a single string (one device per code), not an array.
  • Don't hardcode the
    code
    value unless you need a specific PIN — Seam generates a random code by default.
  • name
    is optional but recommended — it appears in the Seam Console for identification.
  • Code slot limits — some locks have a max number of codes. Check
    device.properties.max_active_codes_supported
    .
  • Decide your failure mode: if the room booking requires access (e.g., locked meeting room), throw on Seam failure. If access is optional, log and continue.
  • 存储
    access_code_id
    ——您需要它来进行更新和删除操作。如果预订模型中没有此字段,请添加一个。如果该字段为
    null
    /
    undefined
    ,说明访问码创建失败,需要重试。
  • 切勿使用全局设备回退——每个房间必须映射到其特定设备。
  • **
    device_id
    **是单个字符串(每个访问码对应一个设备),而非数组。
  • 不要硬编码
    code
    ——除非您需要特定的PIN码,Seam默认会生成随机码。
  • **
    name
    **是可选字段,但建议添加——它会显示在Seam控制台中用于识别。
  • 代码插槽限制——部分锁有最大访问码数量限制,请查看
    device.properties.max_active_codes_supported
  • 确定失败处理模式:如果房间预订需要访问权限(例如:上锁的会议室),则在Seam操作失败时抛出异常。如果访问权限是可选的,则记录日志并继续执行。

4. Update access code on booking changes

4. 修改预订时更新访问码

typescript
if (booking.seamAccessCodeId) {
  await seam.accessCodes.update({
    access_code_id: booking.seamAccessCodeId,
    starts_at: booking.startTime,
    ends_at: booking.endTime
  });
}
python
if booking.seam_access_code_id:
    seam.access_codes.update(
        access_code_id=booking.seam_access_code_id,
        starts_at=booking.start_time,
        ends_at=booking.end_time
    )
typescript
if (booking.seamAccessCodeId) {
  await seam.accessCodes.update({
    access_code_id: booking.seamAccessCodeId,
    starts_at: booking.startTime,
    ends_at: booking.endTime
  });
}
python
if booking.seam_access_code_id:
    seam.access_codes.update(
        access_code_id=booking.seam_access_code_id,
        starts_at=booking.start_time,
        ends_at=booking.end_time
    )

5. Delete access code on cancellation

5. 取消预订时删除访问码

typescript
if (booking.seamAccessCodeId) {
  await seam.accessCodes.delete({
    access_code_id: booking.seamAccessCodeId
  });
}
python
if booking.seam_access_code_id:
    seam.access_codes.delete(
        access_code_id=booking.seam_access_code_id
    )
typescript
if (booking.seamAccessCodeId) {
  await seam.accessCodes.delete({
    access_code_id: booking.seamAccessCodeId
  });
}
python
if booking.seam_access_code_id:
    seam.access_codes.delete(
        access_code_id=booking.seam_access_code_id
    )

6. Add webhook endpoint

6. 添加webhook端点

Follow the existing webhook pattern in the codebase:
typescript
router.post("/seam", (req, res) => {
  const { event_type, ...data } = req.body;
  switch (event_type) {
    case "access_code.set_on_device":
      // Code is on the lock — safe to share with the user
      console.log("Code set:", data.access_code_id);
      break;
    case "access_code.failed_to_set_on_device":
      console.log("Code failed:", data.access_code_id);
      break;
    case "device.disconnected":
      console.log("Lock offline:", data.device_id);
      break;
  }
  res.json({ received: true });
});
遵循代码库中现有的webhook模式:
typescript
router.post("/seam", (req, res) => {
  const { event_type, ...data } = req.body;
  switch (event_type) {
    case "access_code.set_on_device":
      // Code is on the lock — safe to share with the user
      console.log("Code set:", data.access_code_id);
      break;
    case "access_code.failed_to_set_on_device":
      console.log("Code failed:", data.access_code_id);
      break;
    case "device.disconnected":
      console.log("Lock offline:", data.device_id);
      break;
  }
  res.json({ received: true });
});

7. Make functions async

7. 将函数改为异步

Make service functions async and update callers to await them.
If something goes wrong, read
references/troubleshooting.md
. For production readiness, read
references/production-checklist.md
.
将服务函数改为异步,并更新调用者以使用await等待结果。
如果遇到问题,请阅读
references/troubleshooting.md
。如需确保生产环境就绪,请阅读
references/production-checklist.md