mcpField
S
seam-access-codes

seam-access-codes

github.com/seamapiDevelopment246 installsUpdated 26w ago
Repository

About seam-access-codes

seam-access-codes is a coding skill for developers adding Seam time-bound access codes to existing booking systems. It guides direct code changes for creating, updating, deleting codes, and handling related webhooks.

Key features

  1. 01Integrate Seam calls into existing code
  2. 02Create time-bound device access codes
  3. 03Store code IDs for later changes
  4. 04Update and delete codes
  5. 05Add webhook event handling

Use cases

  1. 01Issue a door code when a booking is created
  2. 02Adjust access windows after booking changes
  3. 03Remove a code when a booking is cancelled

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/seamapi/seam-plugin --skill seam-access-codes

After installing, type/seam-access-codesin Claude Code to confirm it loaded.

Bundled filesreferences/production-checklist.mdreferences/troubleshooting.md

Details

From the project README

Seam Access Codes (Lower-level API)

You are an expert Seam integration engineer. Write the integration code directly into the developer's existing codebase.

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.

How Access Codes works

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.

1. Install SDK + initialize

Do NOT pin to a specific version.

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:

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:

import { Seam } from "seam";
const seam = new Seam({ apiKey: process.env.SEAM_API_KEY });
from seam import Seam
seam = Seam(api_key=os.environ["SEAM_API_KEY"])

2. Get the device 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.

3. Create access code on booking creation

Add directly inside the create function. Store the access_code_id on the booking object.

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

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.

4. Update access code on booking changes

if (booking.seamAccessCodeId) {
  await seam.accessCodes.update({
    access_code_id: booking.seamAccessCodeId,
    starts_at: booking.startTime,
    ends_at: booking.endTime
  });
}
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

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

6. Add webhook endpoint

Follow the existing webhook pattern in the codebase:

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

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.

Comments

Markdown supported