Skip to content

Commit 03cd803

Browse files
janangitbook-bot
authored andcommitted
GITBOOK-732: Customer Portal iFrame guide
1 parent bb0d104 commit 03cd803

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

docs/capability-guides/customer-portals/integrate-customer-portal-into-your-application.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,179 @@ Use the `create_portal` endpoint to generate Customer Portals programmatically.
4848
* Use your own IDs: Use `space_key` values that match your internal identifiers.
4949
* Session lifetime: All portal links expire after 7 days. Generate a new portal programmatically each time a customer needs access.
5050

51+
***
52+
53+
## Embedding the Customer Portal as an iFrame
54+
55+
The Seam Customer Portal is a hosted, pre-authenticated interface for managing devices, access codes, reservations, and more. Instead of sharing a magic link with your end-users, you can embed the portal directly inside your own application using an iFrame. This gives your users a seamless experience without leaving your product.
56+
57+
### How it works
58+
59+
When you create a customer portal via the API, Seam returns a `magic_link` URL. This URL can be loaded in a standard HTML `<iframe>`. The portal is fully authenticated through a token embedded in the URL — no additional login is required from the end-user.
60+
61+
Portal links expire after **7 days**. Your backend should generate a fresh link when rendering the page, or when the current link is close to expiring.
62+
63+
### Step 1: Create a portal with `is_embedded: true`
64+
65+
Call the `/customers/create_portal` endpoint from your backend with the `is_embedded` flag set to `true`. This tells the portal to render in a layout optimized for iFrame embedding (no unnecessary navigation that would conflict with your app's own UI).
66+
67+
```bash
68+
curl -X POST https://connect.getseam.com/customers/create_portal \
69+
-H "Authorization: Bearer ${SEAM_API_KEY}" \
70+
-H "Content-Type: application/json" \
71+
-d '{
72+
"customer_data": {
73+
"customer_key": "my-customer-123",
74+
"spaces": [
75+
{
76+
"name": "123 Main Street",
77+
"space_key": "property-123"
78+
}
79+
]
80+
},
81+
"is_embedded": true,
82+
"features": {
83+
"connect": { "exclude": false },
84+
"manage": {
85+
"exclude": false,
86+
"exclude_reservation_management": false,
87+
"exclude_staff_management": true
88+
},
89+
"organize": { "exclude": true },
90+
"configure": { "exclude": true }
91+
}
92+
}'
93+
```
94+
95+
The response includes a `magic_link` object:
96+
97+
```json
98+
{
99+
"magic_link": {
100+
"url": "https://partner-ui.seam.vc/portals/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?token=seam_cst_...",
101+
"customer_key": "my-customer-123",
102+
"expires_at": "2026-03-25T12:00:00.000Z",
103+
"workspace_id": "...",
104+
"created_at": "2026-03-18T12:00:00.000Z"
105+
}
106+
}
107+
```
108+
109+
### Step 2: Embed the URL in an iFrame
110+
111+
Use the `magic_link.url` as the `src` attribute of an iFrame in your frontend. The portal handles authentication automatically via the token in the URL.
112+
113+
```html
114+
<iframe
115+
src="https://partner-ui.seam.vc/portals/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?token=seam_cst_..."
116+
width="100%"
117+
height="800"
118+
frameborder="0"
119+
allow="clipboard-write"
120+
style="border: none; border-radius: 8px;"
121+
></iframe>
122+
```
123+
124+
In practice, your backend generates the `magic_link.url` and passes it to your frontend, which sets it as the iFrame `src`. Don't hardcode the URL — it contains a session token that expires.
125+
126+
### Step 3: Refresh the link before it expires
127+
128+
Portal links are valid for 7 days. If your user keeps a page open for a long time, the embedded portal will eventually expire. Handle this by:
129+
130+
1. **Generating a fresh link on each page load.** This is the simplest approach. Each time your user navigates to the page containing the portal, your backend calls `/customers/create_portal` and returns a new URL.
131+
2. **Tracking expiration client-side.** Store the `expires_at` timestamp and proactively refresh the iFrame `src` before it expires.
132+
133+
You can also use the `/customers/open_portal` endpoint, which reuses an existing portal session if it hasn't expired yet, and creates a new one if it has.
134+
135+
### Configuring which features are visible
136+
137+
The `features` object in the request body controls which sections of the portal your customer sees. Each feature can be included or excluded:
138+
139+
| Feature | What it controls |
140+
| ---------------- | --------------------------------------------------------------------------- |
141+
| `connect` | Connecting new device accounts (e.g., linking an August or Schlage account) |
142+
| `manage` | Managing reservations and staff for properties |
143+
| `organize` | Organizing devices into spaces and groups |
144+
| `configure` | Configuring automation rules for access, climate, and Instant Key |
145+
| `manage_devices` | Legacy device management (use `manage` instead) |
146+
147+
For example, if your product only needs reservation management, you can exclude everything else:
148+
149+
```json
150+
{
151+
"features": {
152+
"connect": { "exclude": true },
153+
"manage": {
154+
"exclude": false,
155+
"exclude_reservation_management": false,
156+
"exclude_staff_management": true
157+
},
158+
"organize": { "exclude": true },
159+
"configure": { "exclude": true }
160+
}
161+
}
162+
```
163+
164+
### Embedding a single reservation view (deep links)
165+
166+
If you want to embed a portal that shows a single reservation — for example, inside a reservation detail page in your PMS — use the `/customers/reservations/create_deep_link` endpoint instead.
167+
168+
```bash
169+
curl -X POST https://connect.getseam.com/customers/reservations/create_deep_link \
170+
-H "Authorization: Bearer ${SEAM_API_KEY}" \
171+
-H "Content-Type: application/json" \
172+
-d '{
173+
"customer_key": "my-customer-123",
174+
"reservation_key": "res-456"
175+
}'
176+
```
177+
178+
This returns a `deep_link.url` that you embed the same way. The portal navigates directly to the reservation page with the navigation UI hidden, so it feels like a native part of your application. Deep links automatically set `is_embedded: true` and `navigation_mode: "restricted"`.
179+
180+
### Localization
181+
182+
Set the `locale` parameter when creating the portal to display it in a different language:
183+
184+
* `en-US` (default)
185+
* `pt-PT` — Portuguese
186+
* `fr-FR` — French
187+
* `it-IT` — Italian
188+
* `es-ES` — Spanish
189+
190+
```json
191+
{
192+
"is_embedded": true,
193+
"locale": "fr-FR",
194+
"customer_data": { "..." }
195+
}
196+
```
197+
198+
### Branding and customization
199+
200+
If you have a customization profile set up (configured through the Seam Console), pass the `customization_profile_id` when creating the portal to apply your branding — colors, logos, and other visual settings.
201+
202+
```json
203+
{
204+
"is_embedded": true,
205+
"customization_profile_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
206+
"customer_data": { "..." }
207+
}
208+
```
209+
210+
### Frequently asked questions
211+
212+
**Do I need to set any special headers or CSP rules on my side?** No. The portal is designed to be embedded and does not set `frame-ancestors` restrictions. As long as your own site doesn't block iFrames in its Content Security Policy, it will work out of the box.
213+
214+
**Can my user interact with the portal inside the iFrame?** Yes. The portal is fully interactive — users can connect accounts, view device status, manage reservations, copy access codes, and everything else the portal supports. Adding `allow="clipboard-write"` to your iFrame tag enables the copy-to-clipboard functionality for access codes.
215+
216+
**What happens when the portal link expires?** The portal will show an error state. Your application should detect this (either by tracking the `expires_at` timestamp or by listening for a page error in the iFrame) and generate a fresh link.
217+
218+
**Can I scope the portal to specific properties or devices?** Yes. Pass `customer_resources_filters` when creating the portal to filter which resources are visible based on their `custom_metadata`. For example, you could show only properties in a specific region or managed by a specific team.
219+
220+
```json
221+
{
222+
"customer_resources_filters": [
223+
{ "field": "region", "operation": "=", "value": "us-west" }
224+
]
225+
}
226+
```

0 commit comments

Comments
 (0)