seam-access-codes
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSeam 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
方法
- Move fast. Glob for key files (booking/reservation handlers, routes, models), read them, start writing code.
- Write code in existing files. Add Seam calls directly into existing service/handler functions. Don't create wrapper services.
- Minimize changes. Only touch files that need Seam calls + webhook route. Install SDK, add import, add calls.
- 快速推进:查找关键文件(预订/预约处理器、路由、模型),读取文件后开始编写代码。
- 在现有文件中编写代码:将Seam调用直接添加到现有服务/处理器函数中,不要创建包装服务。
- 最小化修改:仅修改需要添加Seam调用的文件以及webhook路由。安装SDK、添加导入语句、添加调用。
How Access Codes works
Access Codes的工作原理
You create time-bound access codes directly on specific devices. Each code has a and window. You must store the to update or delete it later. Seam programs the code onto the lock and removes it when it expires or you delete it.
starts_atends_ataccess_code_id您可以直接在特定设备上创建限时访问码。每个访问码都有和时间窗口。您必须存储,以便后续更新或删除该访问码。Seam会将访问码写入锁中,并在过期或您删除时将其移除。
starts_atends_ataccess_code_id1. 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 # RubyCRITICAL for Next.js: at module scope BREAKS . Use a lazy getter:
new Seam()next buildtypescript
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 # RubyNext.js 关键注意事项:在模块作用域使用会导致失败,请使用延迟 getter:
new Seam()next buildtypescript
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 . Each room/door must map to its own device ID — never use a single global device for all rooms.
device_idLook for device IDs in:
- Environment variables per room: ,
SEAM_DEVICE_ROOM_A1, etc.SEAM_DEVICE_ID_ROOM_101 - 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通过定位特定设备。每个房间/门必须映射到其自身的设备ID——切勿为所有房间使用单个全局设备。
device_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 on the booking object.
access_code_idtypescript
// 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_idtypescript
// 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
undefinedInside 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.
undefineddevice_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.
undefinedGotchas
注意事项
- Store — you need it for update and delete. Add a field to the booking model if one doesn't exist. If it's
access_code_id/null, the code failed and needs retry.undefined - Never use a global device fallback — each room must map to its specific device.
- is a single string (one device per code), not an array.
device_id - Don't hardcode the value unless you need a specific PIN — Seam generates a random code by default.
code - is optional but recommended — it appears in the Seam Console for identification.
name - 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 - 不要硬编码值——除非您需要特定的PIN码,Seam默认会生成随机码。
code - ****是可选字段,但建议添加——它会显示在Seam控制台中用于识别。
name - 代码插槽限制——部分锁有最大访问码数量限制,请查看。
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 . For production readiness, read .
references/troubleshooting.mdreferences/production-checklist.md将服务函数改为异步,并更新调用者以使用await等待结果。
如果遇到问题,请阅读。如需确保生产环境就绪,请阅读。
references/troubleshooting.mdreferences/production-checklist.md