Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions codegen/data/resource-sample-definitions/all-resources.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,10 @@
supports_offline_access_codes: false
warnings: []
workspace_id: 5d7f2e1a-9c8b-4f3e-8d2c-1a0b9e8f7c6d
- title: Magic Link
description: A magic link resource.
resource_type: magic_link
- title: Customer Portal
description: A customer portal resource.
resource_type: customer_portal
properties:
building_block_type: connect_accounts
created_at: '2025-06-16T16:54:17.946594Z'
customer_key: My Company
expires_at: '2025-06-17T16:54:17.946594Z'
Expand Down
127 changes: 103 additions & 24 deletions docs/api-reference/_blueprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -27848,8 +27848,8 @@
"draftMessage": "",
"response": {
"responseType": "resource",
"responseKey": "magic_link",
"resourceType": "magic_link",
"responseKey": "customer_portal",
"resourceType": "customer_portal",
"description": "OK",
"actionAttemptType": null,
"batchResourceTypes": null
Expand Down Expand Up @@ -103334,6 +103334,106 @@
"propertyGroups": [],
"resourceSamples": []
},
{
"resourceType": "customer_portal",
"properties": [
{
"name": "created_at",
"description": "Date and time at which the customer portal link was created.",
"isDeprecated": false,
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroupKey": null,
"format": "datetime",
"jsonType": "string"
},
{
"name": "customer_key",
"description": "Customer key for the customer portal.",
"isDeprecated": false,
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroupKey": null,
"format": "string",
"jsonType": "string"
},
{
"name": "expires_at",
"description": "Date and time at which the customer portal link expires.",
"isDeprecated": false,
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroupKey": null,
"format": "datetime",
"jsonType": "string"
},
{
"name": "url",
"description": "URL for the customer portal.",
"isDeprecated": false,
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroupKey": null,
"format": "string",
"jsonType": "string"
},
{
"name": "workspace_id",
"description": "ID of the [workspace](https://docs.seam.co/latest/core-concepts/workspaces) associated with the customer portal.",
"isDeprecated": false,
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroupKey": null,
"format": "id",
"jsonType": "string"
}
],
"description": "Represents a Customer Portal. Customer Portal is a hosted, customizable interface for managing device access. It enables you to embed secure, pre-authenticated access flows into your product—either by sharing a link with users or embedding a view in an iframe.\n\nWith Customer Portal, you no longer need to build out frontend experiences for physical access, thermostats, and sensors. Instead, you can ship enterprise-grade access control experiences in a fraction of the time, while maintaining your product's branding and user experience.\n\nSeam hosts these flows, handling everything from account connection and device mapping to full-featured device control.",
"isDeprecated": false,
"routePath": "/customers",
"deprecationMessage": "",
"isUndocumented": false,
"undocumentedMessage": "",
"isDraft": false,
"draftMessage": "",
"propertyGroups": [],
"resourceSamples": [
{
"title": "Customer Portal",
"description": "A customer portal resource.",
"resource_type": "customer_portal",
"properties": {
"created_at": "2025-06-16T16:54:17.946594Z",
"customer_key": "My Company",
"expires_at": "2025-06-17T16:54:17.946594Z",
"url": "https://se.am/1234",
"workspace_id": "67c58f1f-f148-4415-a63c-dc6c145c6b91"
},
"resource": {
"seam_cli": {
"title": "Seam CLI",
"resource_data": "{\n \"created_at\": \"2025-06-16T16:54:17.946594Z\",\n \"customer_key\": \"My Company\",\n \"expires_at\": \"2025-06-17T16:54:17.946594Z\",\n \"url\": \"https://se.am/1234\",\n \"workspace_id\": \"67c58f1f-f148-4415-a63c-dc6c145c6b91\"\n}",
"resource_data_syntax": "json"
}
}
}
]
},
{
"resourceType": "customization_profile",
"properties": [
Expand Down Expand Up @@ -116376,28 +116476,7 @@
"isDraft": false,
"draftMessage": "",
"propertyGroups": [],
"resourceSamples": [
{
"title": "Magic Link",
"description": "A magic link resource.",
"resource_type": "magic_link",
"properties": {
"building_block_type": "connect_accounts",
"created_at": "2025-06-16T16:54:17.946594Z",
"customer_key": "My Company",
"expires_at": "2025-06-17T16:54:17.946594Z",
"url": "https://se.am/1234",
"workspace_id": "67c58f1f-f148-4415-a63c-dc6c145c6b91"
},
"resource": {
"seam_cli": {
"title": "Seam CLI",
"resource_data": "{\n \"building_block_type\": \"connect_accounts\",\n \"created_at\": \"2025-06-16T16:54:17.946594Z\",\n \"customer_key\": \"My Company\",\n \"expires_at\": \"2025-06-17T16:54:17.946594Z\",\n \"url\": \"https://se.am/1234\",\n \"workspace_id\": \"67c58f1f-f148-4415-a63c-dc6c145c6b91\"\n}",
"resource_data_syntax": "json"
}
}
}
]
"resourceSamples": []
},
{
"resourceType": "noise_threshold",
Expand Down
2 changes: 2 additions & 0 deletions docs/api-reference/_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,8 @@ These items are deprecated.
- `action_attempt`
- `/access_codes/pull_backup_access_code`
- `backup_access_code`
- `/customers/create_portal`
- `magic_link`
- `/events/get`
- `message`
- `/locks/get`
Expand Down
77 changes: 77 additions & 0 deletions docs/api-reference/customers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,83 @@
**Early Access Preview.** The customers API is currently in Alpha. We're actively developing it and seeking early feedback at [support@seam.co](mailto:support@seam.co). Expect breaking changes as we refine the design.
{% endhint %}

## The customer_portal Object

- [Properties](./#properties)
- [Endpoints](./#endpoints)


Represents a Customer Portal. Customer Portal is a hosted, customizable interface for managing device access. It enables you to embed secure, pre-authenticated access flows into your product—either by sharing a link with users or embedding a view in an iframe.

With Customer Portal, you no longer need to build out frontend experiences for physical access, thermostats, and sensors. Instead, you can ship enterprise-grade access control experiences in a fraction of the time, while maintaining your product's branding and user experience.

Seam hosts these flows, handling everything from account connection and device mapping to full-featured device control.

{% tabs %}
{% tab title="Customer Portal" %}

A customer portal resource.

```json
{
"created_at": "2025-06-16T16:54:17.946594Z",
"customer_key": "My Company",
"expires_at": "2025-06-17T16:54:17.946594Z",
"url": "https://se.am/1234",
"workspace_id": "67c58f1f-f148-4415-a63c-dc6c145c6b91"
}
```
{% endtab %}
{% endtabs %}

---
## Properties

**`created_at`** *Datetime*

Date and time at which the customer portal link was created.




---

**`customer_key`** *String*

Customer key for the customer portal.




---

**`expires_at`** *Datetime*

Date and time at which the customer portal link expires.




---

**`url`** *String*

URL for the customer portal.




---

**`workspace_id`** *UUID*

ID of the [workspace](https://docs.seam.co/latest/core-concepts/workspaces) associated with the customer portal.




---

## Endpoints


Expand Down
3 changes: 1 addition & 2 deletions docs/api-reference/customers/create_portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Navigation mode for the portal. 'restricted' tells frontend to hide navigation U

{% hint style="success" %}
Returns:
**[magic\_link](./../unstable_partner/building_blocks)**
**[customer\_portal](.)**

{% endhint %}

Expand All @@ -430,7 +430,6 @@ Returns:

```json
{
"building_block_type": "connect_accounts",
"created_at": "2025-06-16T16:54:17.946594Z",
"customer_key": "My Company",
"expires_at": "2025-06-17T16:54:17.946594Z",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Create property listings with a `region` field in `custom_metadata`, then filter
{% tabs %}
{% tab title="JavaScript" %}
```javascript
const { magic_link } = await seam.customers.createPortal({
const { customer_portal } = await seam.customers.createPortal({
customer_data: {
customer_key: "customer_123",
property_listings: [
Expand All @@ -71,7 +71,7 @@ const { magic_link } = await seam.customers.createPortal({
});

// The portal will only show "Lisbon Apartment"
console.log(magic_link.url);
console.log(customer_portal.url);
```
{% endtab %}

Expand Down Expand Up @@ -112,7 +112,7 @@ When you provide multiple filters, a resource must match all of them. Here, only
{% tabs %}
{% tab title="JavaScript" %}
```javascript
const { magic_link } = await seam.customers.createPortal({
const { customer_portal } = await seam.customers.createPortal({
customer_data: {
customer_key: "customer_123",
property_listings: [
Expand Down Expand Up @@ -140,7 +140,7 @@ const { magic_link } = await seam.customers.createPortal({
});

// Only "Premium Villa" is visible in the portal
console.log(magic_link.url);
console.log(customer_portal.url);
```
{% endtab %}

Expand Down Expand Up @@ -187,7 +187,7 @@ Filter by a boolean `custom_metadata` value, such as showing only premium listin
{% tabs %}
{% tab title="JavaScript" %}
```javascript
const { magic_link } = await seam.customers.createPortal({
const { customer_portal } = await seam.customers.createPortal({
customer_data: {
customer_key: "customer_123",
property_listings: [
Expand All @@ -209,7 +209,7 @@ const { magic_link } = await seam.customers.createPortal({
});

// Only "Premium Villa" is visible
console.log(magic_link.url);
console.log(customer_portal.url);
```
{% endtab %}

Expand Down Expand Up @@ -250,7 +250,7 @@ Filters also apply to reservations. Here, only reservations that are both premiu
{% tabs %}
{% tab title="JavaScript" %}
```javascript
const { magic_link } = await seam.customers.createPortal({
const { customer_portal } = await seam.customers.createPortal({
customer_data: {
customer_key: "customer_123",
property_listings: [
Expand Down Expand Up @@ -287,7 +287,7 @@ const { magic_link } = await seam.customers.createPortal({
});

// Only "Premium EU Reservation" is visible in the portal
console.log(magic_link.url);
console.log(customer_portal.url);
```
{% endtab %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ The Seam Customer Portal is a hosted, pre-authenticated interface for managing d

### How it works

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.
When you create a customer portal via the API, Seam returns a `customer_portal` 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.

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.

Expand Down Expand Up @@ -92,11 +92,11 @@ curl -X POST https://connect.getseam.com/customers/create_portal \
}'
```

The response includes a `magic_link` object:
The response includes a `customer_portal` object:

```json
{
"magic_link": {
"customer_portal": {
"url": "https://partner-ui.seam.vc/portals/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?token=seam_cst_...",
"customer_key": "my-customer-123",
"expires_at": "2026-03-25T12:00:00.000Z",
Expand All @@ -108,7 +108,7 @@ The response includes a `magic_link` object:

### Step 2: Embed the URL in an iFrame

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.
Use the `customer_portal.url` as the `src` attribute of an iFrame in your frontend. The portal handles authentication automatically via the token in the URL.

```html
<iframe
Expand All @@ -121,7 +121,7 @@ Use the `magic_link.url` as the `src` attribute of an iFrame in your frontend. T
></iframe>
```

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.
In practice, your backend generates the `customer_portal.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.

### Step 3: Refresh the link before it expires

Expand Down
Loading
Loading