Seam 2.0.0-beta.5

This is a prerelease version of Seam.
dotnet add package Seam --version 2.0.0-beta.5
                    
NuGet\Install-Package Seam -Version 2.0.0-beta.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Seam" Version="2.0.0-beta.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Seam" Version="2.0.0-beta.5" />
                    
Directory.Packages.props
<PackageReference Include="Seam" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Seam --version 2.0.0-beta.5
                    
#r "nuget: Seam, 2.0.0-beta.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Seam@2.0.0-beta.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Seam&version=2.0.0-beta.5&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Seam&version=2.0.0-beta.5&prerelease
                    
Install as a Cake Tool

Seam C#

GitHub Actions

SDK for the Seam API written in C#.

Upgrading from v1? See MIGRATION.md.

Installation

Use NuGet to install.

dotnet add package Seam

Usage

using Seam;

var seam = new SeamClient(apiKey: "YOUR_API_KEY");

var devices = await seam.Devices.ListAsync();

Console.WriteLine($"First device: {devices[0].DisplayName}");

var device = await seam.Locks.GetAsync(new() { DeviceId = devices[0].DeviceId });

Endpoint methods are async, take a single request object, and accept a CancellationToken. Required parameters are required members of the request object, so a missing one is a compile error rather than a server round trip. Request objects are always constructed with named properties (typically via a target-typed new()), so adding or reordering API parameters never breaks your code.

Authentication

Authenticate with an API key, which is scoped to a single workspace:

var seam = new SeamClient(apiKey: "YOUR_API_KEY");
// or
var seam = SeamClient.FromApiKey("YOUR_API_KEY");

Or with a personal access token and the workspace it acts on:

var seam = SeamClient.FromPersonalAccessToken("YOUR_PAT", "YOUR_WORKSPACE_ID");

When no credential is passed, the client reads SEAM_API_KEY or SEAM_PERSONAL_ACCESS_TOKEN plus SEAM_WORKSPACE_ID from the environment, and the endpoint falls back to SEAM_ENDPOINT:

var seam = new SeamClient();

To list and create workspaces before having one in scope, use the workspace-less client:

var seam = new SeamWithoutWorkspaceClient(personalAccessToken: "YOUR_PAT");
var workspaces = await seam.Workspaces.ListAsync();

Action attempts

Some endpoints, e.g. unlocking a door, return an action attempt tracking the requested action. By default, the SDK polls the action attempt until it succeeds and returns the finished attempt, raising SeamActionAttemptFailedException when it fails and SeamActionAttemptTimeoutException when it is still pending after 10 seconds:

var actionAttempt = await seam.Locks.UnlockDoorAsync(new() { DeviceId = deviceId });

Each action attempt deserializes to a subclass for its action_type and status pair, e.g. ActionAttemptUnlockDoorSuccess. The Error and Result properties are declared only on the status subclass that populates them, so pattern match on the subclass to read them:

var actionAttempt = await seam.Locks.UnlockDoorAsync(
    new() { DeviceId = deviceId },
    waitForActionAttempt: false
);

switch (actionAttempt)
{
    case ActionAttemptUnlockDoorSuccess success:
        Console.WriteLine(success.Result.WasConfirmedByDevice);
        break;
    case ActionAttemptUnlockDoorError error:
        Console.WriteLine(error.Error.Message);
        break;
    case ActionAttemptUnlockDoorPending:
        Console.WriteLine("Still pending");
        break;
}

Configure or disable waiting per client or per call with ActionAttemptWait:

// Do not wait: get the pending action attempt back immediately.
var seam = new SeamClient(new SeamClientOptions
{
    ApiKey = "YOUR_API_KEY",
    WaitForActionAttempt = false,
});

// Wait longer for this one call.
var actionAttempt = await seam.Locks.UnlockDoorAsync(
    new() { DeviceId = deviceId },
    waitForActionAttempt: new ActionAttemptWait
    {
        Timeout = TimeSpan.FromSeconds(30),
        PollingInterval = TimeSpan.FromSeconds(2),
    }
);

Pagination

Every paginated list endpoint offers a ListPager returning a SeamPaginator:

var pages = seam.Devices.ListPager(new() { Limit = 20 });

// Iterate every item lazily.
await foreach (var device in pages.Flatten())
{
    Console.WriteLine(device.DeviceId);
}

// Or fetch pages by hand.
var (devices, pagination) = await pages.FirstPageAsync();
if (pagination.HasNextPage)
{
    var (moreDevices, _) = await pages.NextPageAsync(pagination.NextPageCursor!);
}

// Or collect everything into one list.
var allDevices = await pages.FlattenToListAsync();

To resume pagination later, store pagination.NextPageCursor and pass it to NextPageAsync on a new pager with the same request parameters.

Error Handling

Seam API errors raise a typed exception carrying the Seam error code, HTTP status code, and the seam-request-id to include in support requests.

Validation errors

When the API rejects a request because a parameter is invalid, it throws a SeamHttpInvalidInputException. Look up messages for a parameter you are already rendering, for example a field in a form:

try
{
    await seam.Devices.ListAsync(new() { DeviceIds = ["not-a-uuid"] });
}
catch (SeamHttpInvalidInputException exception)
{
    foreach (var message in exception.GetValidationErrorMessages("device_ids"))
        Console.WriteLine(message);
}

Or read every parameter that failed validation to summarize the request:

foreach (var validationError in exception.ValidationErrors)
{
    Console.WriteLine(
        $"{validationError.ParameterName}: {string.Join(", ", validationError.ErrorMessages)}"
    );
}

Every SDK exception derives from SeamException. A response that is not a Seam error, e.g. from a gateway, surfaces as the standard HttpRequestException.

Retries and timeouts

Idempotent requests are retried twice on transport errors, timeouts, 429, and 5xx responses with exponential backoff, honoring Retry-After. POST and PATCH requests are never retried, so a retry can never duplicate a write. Each attempt times out after 30 seconds. Both are configurable:

var seam = new SeamClient(new SeamClientOptions
{
    ApiKey = "YOUR_API_KEY",
    MaxRetries = 0,
    Timeout = TimeSpan.FromSeconds(60),
});

Setting a value to null

The Seam API distinguishes three states for an updatable parameter: omitted (leave the stored value unchanged), null (unset the stored value), and a value (set it). C#'s null means omitted; the SDK removes null parameters from the request entirely. Where the Seam API documents a parameter as nullable, the request property is an Optional<T> that also accepts the explicit Null.Value sentinel:

// Omits every optional parameter, leaving stored values unchanged.
await seam.Thermostats.UpdateAsync(new() { DeviceId = deviceId });

// Unsets the sync key of the stored custom metadata.
await seam.Devices.UpdateAsync(new()
{
    DeviceId = deviceId,
    CustomMetadata = new Dictionary<string, object?> { ["sync"] = Null.Value },
});

Webhooks

Verify and parse incoming Seam webhook events with SeamWebhook:

var webhook = new SeamWebhook(Environment.GetEnvironmentVariable("SEAM_WEBHOOK_SECRET")!);

var seamEvent = webhook.Verify(requestBody, requestHeaders);

if (seamEvent is Seam.Models.EventDeviceConnected connected)
    Console.WriteLine(connected.DeviceId);

Advanced usage

Calling the API directly

The HttpClient the SDK sends requests with is exposed as seam.Client, fully configured with the endpoint, authorization, retries, and timeouts:

var response = await seam.Client.GetAsync("/devices/list");

To supply your own fully configured client instead, use SeamClient.FromHttpClient, or pass an HttpMessageHandler to replace only the innermost transport while keeping the SDK's pipeline:

var seam = new SeamClient(new SeamClientOptions
{
    ApiKey = "YOUR_API_KEY",
    HttpMessageHandler = myHandler,
});

Serializing URL search params

The Seam API parses URL search params as complex types. The SDK serializes the params of every endpoint the Seam API prefers to receive as a GET or DELETE this way. If you call the API with your own HTTP client, StrictUrlSearchParamsSerializer is exported for that purpose. The _strict=true parameter is added to any non-empty query so the Seam API uses strict, schema-aware parsing.

var query = StrictUrlSearchParamsSerializer.Serialize(
    new Dictionary<string, object?> { ["device_ids"] = new[] { "a", "b" } }
);

Development and testing

Quickly run all tests with

just test

The tests run against @seamapi/fake-seam-connect; run npm install first. Generated code under src/Seam/Routes and src/Seam/Models is produced by npm run generate from @seamapi/types and must not be edited by hand.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

  • net8.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0-beta.5 37 8/27/2026
2.0.0-beta.4 35 8/27/2026
2.0.0-beta.3 52 8/20/2026
2.0.0-beta.2 51 8/20/2026
2.0.0-beta.1 62 8/20/2026
1.5.0 77 8/26/2026
1.4.0 297 8/20/2026
1.3.0 98 8/19/2026
1.2.0 950 8/14/2026
1.1.0 103 8/11/2026
1.0.1 671 8/6/2026
1.0.0 121 7/31/2026
0.99.0 238 7/29/2026
0.98.0 181 7/24/2026
0.97.0 126 7/23/2026
0.96.0 8,965 1/21/2026
0.95.0 729 9/18/2025
0.94.1 454 9/17/2025
0.94.0 2,059 9/5/2025
0.93.0 274 9/5/2025
Loading failed