How to connect a smart lock to your app
Connect a lock in 60 seconds
1# 1. Create a Connect Webview. The response includes a url for your user.
2curl -X POST "https://connect.getseam.com/connect_webviews/create" \
3 -H "Authorization: Bearer $SEAM_API_KEY" \
4 -H "Content-Type: application/json" \
5 -d '{
6 "accepted_providers": ["schlage", "yale", "august", "smartthings"],
7 "custom_redirect_url": "https://example.com/locks/connected"
8 }'
9
10# 2. Open the url from the response in an iframe or a new window. Your user
11# logs in to their lock account there; the credentials never reach you.
12
13# 3. After the redirect, confirm the connection succeeded.
14curl -X POST "https://connect.getseam.com/connect_webviews/get" \
15 -H "Authorization: Bearer $SEAM_API_KEY" \
16 -H "Content-Type: application/json" \
17 -d '{ "connect_webview_id": "c4c30885-ec87-4b31-8d7b-9bc0678fa028" }'Build it with your AI agent
- Reservation AutomationsPush reservations to Seam and let it manage codes across the booking lifecycle.
- Access GrantsPer-entrance, per-credential access: PIN codes, mobile keys and Instant Keys.
- Access CodesDirect, manual control of individual time-bound codes on a specific device.
1. Install the plugin
/plugin marketplace add seamapi/seam-plugin
/plugin install seam@seamapiThe Claude Code install also adds our documentation MCP server, so the agent can look up endpoints and device capabilities while it works. Cursor, Codex and other agents get the skills through npx skills add and can add the MCP server there separately.
2. Paste this prompt
Add smart lock connection to my app with Seam.
Build the flow in three parts:
1. A backend endpoint that calls /connect_webviews/create with accepted_providers for the brands we support and a custom_redirect_url back into our app, then returns the webview url to the client.
2. A frontend page that opens that url and, after the redirect, calls the backend to verify the webview with /connect_webviews/get: treat login_successful true as connected, anything else as a retry.
3. A sync step that lists the connected account's devices with /devices/list, stores device_id, display_name and the capability flags we care about, and re-runs when we get device lifecycle events.
Use a sandbox workspace and its virtual devices for the tests, keep the API key server-side only, and follow my existing routing and error-handling conventions.What you can build
Onboarding that survives brand variety
Property managers show up with August in one building and Schlage in the next. One webview handles every brand's login, multifactor prompts and error states, so onboarding is the same flow no matter what is on the door.
A device list that stays current
Once an account is connected, locks added to it later flow into Seam without another login. Your sync job lists devices and picks up the new hardware.
Capability-aware UI
Every device carries capability flags, so your app can show a PIN pad only for locks that take codes and a remote unlock button only where it will work. No brand matrix to maintain by hand.
How it works
Install the SDK and export your API key
Create a workspace in Seam Console, generate an API key under Developer > API Keys, and export it as SEAM_API_KEY. Start in a sandbox workspace: it comes with virtual locks, so you can run this whole flow before touching real hardware.
1npm i seam 2 3# The SDK picks this up automatically. 4export SEAM_API_KEY=seam_test_your_sandbox_api_keyCreate a Connect Webview
One call from your backend. accepted_providers narrows the brand list to what your product supports; leaving it off shows every stable brand. The response carries a url for your user and a connect_webview_id you keep, because the id is how you find out what happened after they finish.
1const connectWebview = await seam.connectWebviews.create({ 2 // Only the brands your product supports. Omit this to show all stable brands. 3 accepted_providers: ["schlage", "yale", "august", "smartthings"], 4 // Where the webview sends the user when they finish. 5 custom_redirect_url: "https://example.com/locks/connected", 6}) 7 8// Store the id so you can verify the outcome after the redirect. 9console.log(connectWebview.connect_webview_id) 10console.log(connectWebview.url)Open the url and let your user log in
Open the url in an iframe or a new window. Your user picks their brand and signs in with their own account. Seam runs the credential validation and any multifactor step, and sends them to your custom_redirect_url when the connection is made. Their password never passes through your servers, which keeps that login out of your security scope.
Verify the connection and list the devices
Fetch the webview by id. status authorized with login_successful true means the account is connected, and connected_account_id scopes a device list to exactly what this user linked. Read the capability flags off each device before you offer actions in your UI: a lock that cannot take codes should never show a PIN screen.
1const finished = await seam.connectWebviews.get({ 2 connect_webview_id: storedConnectWebviewId, 3}) 4 5// "authorized" and login_successful arrive together when the user completes 6// the flow. Anything else means they closed it or the login failed. 7if (finished.status === "authorized" && finished.login_successful) { 8 console.log(finished.connected_account_id) 9}React to new hardware over time
Accounts change after onboarding: locks get added, batteries die, someone renames the front door. Re-list devices on a schedule or subscribe to device lifecycle events with a webhook, and treat the device list as state you sync instead of a one-time import.
1const devices = await seam.devices.list({ 2 connected_account_id: finished.connected_account_id, 3}) 4 5// Capability flags tell you what each lock can do before you offer it in 6// your UI. true works today, false means blocked right now, absent means 7// the model never does it. 8for (const device of devices) { 9 console.log( 10 device.display_name, 11 device.can_remotely_unlock, 12 device.can_program_online_access_codes, 13 ) 14}
Frequently asked questions
Do my users need a Seam account?
No. They log in with their existing lock account, the one from the lock's own app. Seam sits between your app and the brand's cloud; your users never see Seam.
Which smart lock brands can my users connect?
Dozens, including August, Yale, Schlage, Kwikset, TTLock, Nuki, Tedee and igloohome, plus access control systems like Salto and Brivo. The supported devices page lists every model, and accepted_providers controls which ones your webview offers.
Should I open the webview in an iframe or a new window?
Both work across modern browsers. An iframe keeps the user in your flow and suits onboarding wizards; a new window is simpler when your app is mobile web. Either way, the custom_redirect_url brings the user back when they finish.
What happens when my user adds a lock to their account later?
New devices on a connected account flow into Seam without another webview. List devices again, or subscribe to device events, and the new lock is there with its capability flags set.
How do I test this without buying a lock?
Sandbox workspaces come with virtual devices for every major brand. The webview flow, the device list and the capability flags all behave like production, so you can build the whole feature before hardware arrives.
What do I store on my side?
The connect_webview_id while the flow is in progress, then the connected_account_id and the device_ids you care about. Keep the API key server-side; the webview url is the only thing the browser needs.
Related
- Connect Webviews documentationThe full flow, customization options, and every field on the connect_webview object.
- How to generate temporary access codes programmaticallyThe next step once locks are connected: issue time-bound PIN codes from your app.
- Smart locks that support remote unlockEvery model you can unlock over the internet once its account is connected.
- Every device and system Seam supportsSearch the whole device database by brand, device type and capability.