.NET SDK Reference

Package: CpodSdk
NuGet: nuget.org/packages/CpodSdk
Source: github.com/cpod-ai/cpod-sdk/tree/main/sdks/dotnet
Target Frameworks: net6.0, net7.0, net8.0, netstandard2.1


Installation

dotnet add package CpodSdk
<!-- Or in your .csproj -->
<PackageReference Include="CpodSdk" Version="1.*" />

Client Initialization

using Cpod;
 
// Reads CPOD_API_KEY from environment variables
var client = new CpodClient();
 
// With explicit options
var client = new CpodClient(new CpodOptions
{
    ApiKey  = Environment.GetEnvironmentVariable("CPOD_API_KEY")
              ?? throw new InvalidOperationException("CPOD_API_KEY is required"),
    BaseUrl = new Uri("https://api.cyberpod.app/v1"),
    Timeout = TimeSpan.FromSeconds(30),
    MaxRetries = 3,
});

CpodClient implements IDisposable. For long-lived applications, register it as a singleton:

// In Program.cs / Startup.cs (ASP.NET Core)
builder.Services.AddSingleton<CpodClient>();
// or with configuration
builder.Services.AddSingleton(sp => new CpodClient(new CpodOptions
{
    ApiKey = builder.Configuration["CpodSdk:ApiKey"],
}));

PodServiceclient.Pods

List pods

var result = await client.Pods.ListAsync(new PodListRequest
{
    TenantId = "acme-corp",
    Status   = PodStatus.Running,
    Region   = "us-east-1",
    Limit    = 50,
}, cancellationToken);
 
foreach (var pod in result.Data)
{
    Console.WriteLine($"{pod.Id}: {pod.Name} [{pod.Status}]");
}
// result.NextCursor — pass for next page

Get a pod

var pod = await client.Pods.GetAsync("pod_01jm8xk2y3z", cancellationToken);

Create a pod

var pod = await client.Pods.CreateAsync(new PodCreateRequest
{
    TenantId = "acme-corp",
    Name     = "my-worker",
    Image    = "cpod/worker:latest",
    Cpu      = 2.0,
    MemoryMb = 4096,
    Region   = "us-east-1",
    Env      = new Dictionary<string, string> { ["NODE_ENV"] = "production" },
    Labels   = new Dictionary<string, string> { ["team"] = "platform" },
}, cancellationToken);

Update a pod

var pod = await client.Pods.UpdateAsync("pod_01jm8xk2y3z", new PodUpdateRequest
{
    Name   = "renamed-worker",
    Labels = new Dictionary<string, string> { ["app"] = "worker-v2" },
}, cancellationToken);

Delete a pod

await client.Pods.DeleteAsync("pod_01jm8xk2y3z", cancellationToken);

Lifecycle operations

await client.Pods.StartAsync("pod_01jm8xk2y3z", cancellationToken);
await client.Pods.StopAsync("pod_01jm8xk2y3z", new PodStopRequest
{
    GracePeriodSeconds = 30,
}, cancellationToken);
await client.Pods.RestartAsync("pod_01jm8xk2y3z", cancellationToken);

Stream logs

await foreach (var line in client.Pods.StreamLogsAsync("pod_01jm8xk2y3z", new PodLogsRequest
{
    Follow = true,
    Tail   = 100,
}, cancellationToken))
{
    Console.WriteLine(line);
}

Auto-paginated IAsyncEnumerable

await foreach (var pod in client.Pods.ListAllAsync(new PodListRequest
{
    TenantId = "acme-corp",
}, cancellationToken))
{
    await ProcessPodAsync(pod);
}

TenantServiceclient.Tenants

// List
var tenants = await client.Tenants.ListAsync(new TenantListRequest { Limit = 50 }, ct);
 
// Get
var tenant = await client.Tenants.GetAsync("acme-corp", ct);
 
// Create
var tenant = await client.Tenants.CreateAsync(new TenantCreateRequest
{
    Id       = "acme-corp",
    Name     = "Acme Corporation",
    Plan     = TenantPlan.Enterprise,
    Region   = "us-east-1",
    Metadata = new Dictionary<string, string> { ["salesforce_id"] = "SF-001234" },
}, ct);
 
// Update
var tenant = await client.Tenants.UpdateAsync("acme-corp", new TenantUpdateRequest
{
    Name = "Acme Corp (Updated)",
}, ct);
 
// Member management
await client.Tenants.AddMemberAsync("acme-corp", new AddMemberRequest
{
    UserId = "user_123",
    Role   = TenantRole.Admin,
}, ct);
await client.Tenants.RemoveMemberAsync("acme-corp", "user_123", ct);
 
// Policy
await client.Tenants.SetPolicyAsync("acme-corp", new TenantPolicy
{
    MaxPods        = 100,
    AllowedRegions = new[] { "us-east-1", "eu-west-1" },
    RequireLabels  = new[] { "team", "env" },
    IpAllowlist    = new[] { "10.0.0.0/8" },
}, ct);

EventServiceclient.Events

Query events

var result = await client.Events.ListAsync(new EventListRequest
{
    TenantId = "acme-corp",
    Type     = "pod.started",
    Since    = DateTimeOffset.UtcNow.AddDays(-1),
    Limit    = 100,
}, ct);

SSE subscription

await foreach (var evt in client.Events.SubscribeAsync(new EventSubscribeRequest
{
    TenantId = "acme-corp",
    Types    = new[] { "pod.started", "pod.stopped", "pod.error" },
}, ct))
{
    Console.WriteLine($"[{evt.Timestamp:u}] {evt.Type}: {evt.ResourceId}");
    if (evt.Type == "pod.error")
        await AlertOnCallAsync(evt);
}

WebSocket subscription

using var ws = await client.Events.ConnectWebSocketAsync(new EventSubscribeRequest
{
    TenantId = "acme-corp",
    Types    = new[] { "pod.*" },
}, ct);
 
await foreach (var evt in ws.ReceiveAsync(ct))
{
    Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(evt));
}

Record Types

All response models are C# record types with init-only properties.

public sealed record Pod
{
    public required string Id          { get; init; }
    public required string TenantId    { get; init; }
    public required string Name        { get; init; }
    public required PodStatus Status   { get; init; }
    public required string Image       { get; init; }
    public required double Cpu         { get; init; }
    public required int MemoryMb       { get; init; }
    public required string Region      { get; init; }
    public IReadOnlyDictionary<string, string> Env    { get; init; } = new Dictionary<string, string>();
    public IReadOnlyDictionary<string, string> Labels { get; init; } = new Dictionary<string, string>();
    public required DateTimeOffset CreatedAt  { get; init; }
    public required DateTimeOffset UpdatedAt  { get; init; }
    public DateTimeOffset? StartedAt          { get; init; }
    public DateTimeOffset? StoppedAt          { get; init; }
}
 
public enum PodStatus { Pending, Running, Stopped, Error, Terminating }

Error Handling

using Cpod.Errors;
 
try
{
    var pod = await client.Pods.GetAsync("pod_nonexistent", ct);
}
catch (NotFoundException ex)
{
    Console.WriteLine($"Not found: {ex.Message}");
}
catch (ValidationException ex)
{
    foreach (var (field, msg) in ex.Fields)
        Console.WriteLine($"  {field}: {msg}");
}
catch (AuthException ex)
{
    Console.WriteLine($"Auth error ({ex.Status}): {ex.Message}");
}
catch (RateLimitException ex)
{
    Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter}s");
    await Task.Delay(TimeSpan.FromSeconds(ex.RetryAfter), ct);
}
catch (CpodException ex)
{
    Console.WriteLine($"API error {ex.Status} [{ex.Code}]: {ex.Message}");
}

All *Async methods accept an optional CancellationToken. Pass your request’s cancellation token to propagate graceful cancellation through to the HTTP layer.


Dependency Injection (ASP.NET Core)

// Program.cs
builder.Services.AddHttpClient<CpodClient>();
builder.Services.Configure<CpodOptions>(builder.Configuration.GetSection("CpodSdk"));
builder.Services.AddSingleton(sp =>
{
    var opts = sp.GetRequiredService<IOptions<CpodOptions>>().Value;
    return new CpodClient(opts);
});
 
// appsettings.json
{
  "CpodSdk": {
    "ApiKey": "cpod_live_xxx",
    "BaseUrl": "https://api.cyberpod.app/v1"
  }
}