The .NET SDK ships five modality classes plus a raw MoQT client, all under the ClutchCall.SDK namespace.
using ClutchCall.SDK;
// Voice, Streams, Robotics, Games, Data, Moqt
All modality clients implement IAsyncDisposable.

Voice

var v = new Voice(
    baseUrl: "https://app.clutchcall.dev",
    apiKey:  Environment.GetEnvironmentVariable("CLUTCHCALL_API_KEY"),
    orgId:   "org_abc");

Calls

var call = await v.Calls.OriginateAsync(new Voice.OriginateArgs {
    To       = "+15551234567",
    From     = "+15558675309",
    TrunkId  = "trunk_main",
    Agent    = "healthcare-assistant",
});

await v.Calls.GetAsync(sid);
await v.Calls.ListAsync(orgId, cursor, limit);
await v.Calls.TransferAsync(sid, new Voice.TransferArgs { To = "+1..." });
await v.Calls.HangupAsync(sid);
await v.Calls.TerminateAsync(sid);
Call:
call.Sid    : string
call.Status : string
call.From   : string
call.To     : string

await call.RefreshAsync();
await call.HangupAsync();
await call.TransferAsync(args);
call.OnStatus(action);

AudioBridge

var bridge = await v.AudioBridge.AttachAsync(call.Sid,
    new Voice.AudioBridgeOpts {
        Codec    = Voice.Codec.Opus,
        OnUplink = (frame, tsUs) => myAsr.Feed(frame),
    });

bridge.PublishUplink(frame);
bridge.PublishDownlink(frame);
bridge.OnUplink(cb);
bridge.OnDownlink(cb);
await bridge.DisposeAsync();
Codec: Opus, Pcm16, G711ULaw, G711ALaw.

Streams

var s = new Streams("https://app.clutchcall.dev", apiKey, "org_abc");

Control plane

var created = await s.LiveInputs.CreateAsync(new Streams.CreateLiveInput {
    Name = "My Show",
});
string streamKey = created.StreamKey;          // returned ONCE

await s.LiveInputs.GetAsync(id);
await s.LiveInputs.ListAsync(orgId, cursor, limit);
await s.LiveInputs.RotateStreamKeyAsync(id);
await s.LiveInputs.DeleteAsync(id);

await s.SigningKeys.CreateAsync(orgId, label);
await s.SigningKeys.ListAsync(orgId);
await s.SigningKeys.RetireAsync(id);

await s.ApiKeys.CreateAsync(orgId, label, scopes);
await s.ApiKeys.ListAsync(orgId);
await s.ApiKeys.RevokeAsync(id);

await s.Webhooks.CreateAsync(orgId, url, events);
await s.Webhooks.ListAsync(orgId);
await s.Webhooks.DeleteAsync(id);
await s.Events.ListDeliveriesAsync(webhookId, cursor, limit);

await s.Analytics.ViewerMinutesAsync(orgId, from, to, groupBy);
await s.Analytics.PopCacheAsync(orgId, from, to);
LiveInput:
input.ExternalInputId : string
await input.SignedPlaybackUrlAsync(ttlSeconds, scopes); // → SignedPlaybackUrl

Data plane

var pub = await Streams.BroadcastPublisher.OpenAsync(new Streams.PublisherArgs {
    InputId = input.ExternalInputId, StreamKey = streamKey,
    Codecs  = new Streams.Codecs { Video = "avc1.42E01F", Audio = "opus" },
});
pub.Write(chunk);
await pub.CloseAsync(Streams.CloseReason.Complete);

var viewer = await Streams.BroadcastViewer.OpenAsync(signedUrl, new Streams.ViewerOpts {
    OnChunk = (init, chunk) => { /* ... */ },
    OnClose = (reason)      => { /* ... */ },
});
await viewer.DisposeAsync();

Robotics

var r = new Robotics(
    relayHost: "relay.clutchcall.dev",
    token:     Environment.GetEnvironmentVariable("CLUTCHCALL_RELAY_TOKEN"),
    robotId:   "turtlebot-7");
var qos = new Robotics.QoSProfile {
    Reliability = Robotics.Reliability.Reliable,
    Depth       = 10,
};

var pub = await r.PublishTelemetryAsync("odom",
    "nav_msgs/msg/Odometry", qos);
pub.Write(cdrBytes);
await pub.DisposeAsync();

var cmd = await r.PublishCommandAsync("cmd_vel",
    "geometry_msgs/msg/Twist", null);

var sub = await r.SubscribeTelemetryAsync("odom", null,
    (payload, typeName) => { /* ... */ });
await sub.DisposeAsync();
Reliability: BestEffort, Reliable. Durability: Volatile, TransientLocal.

Games

var g = new Games(
    relayHost: "relay.clutchcall.dev",
    token:     token,
    roomId:    "duel-42",
    playerId:  "alice"); // null for authority

Player

var stateSub = g.SubscribeState(bytes => Render(bytes));
var input    = await g.PublishInputAsync();
input.Write(SerializeInput(localInput));

var events = await g.PublishEventAsync();
events.Send(new { kind = "chat", text = "gg" });
g.SubscribeEvents((evt, fromPlayerId) => Dispatch(evt));

Authority (playerId: null)

var state = await g.PublishStateAsync(new Games.StateOpts { TickHz = 30 });
state.Write(SerializeState(world));

g.SubscribeInputs((playerId, payload) => ApplyInput(playerId, payload));
g.SubscribeEvents((evt, fromPlayerId) => { /* ... */ });

Data

var d = new Data(
    relayHost: "relay.clutchcall.dev",
    token:     Environment.GetEnvironmentVariable("CLUTCHCALL_DATA_TOKEN"),
    clientId:  "device-7");
await d.PublishAsync(
    topic:   "sensors/room1/temperature",
    payload: Encoding.UTF8.GetBytes("23.5"),
    reliable: false,
    retain:   false);

var sub = await d.SubscribeAsync("sensors/+/temperature",
    msg => Console.WriteLine($"{msg.Topic}{msg.FromClientId} = " +
                             $"{Encoding.UTF8.GetString(msg.Payload)}"));
await sub.DisposeAsync();
Data.Message:
msg.Topic        : string
msg.Payload      : byte[]
msg.FromClientId : string
msg.Retain       : bool
msg.TsUs         : ulong

Realtime Tracks

See Realtime Tracks. Common methods:
var client = await Moqt.Client.ConnectAsync("quic://relay.clutchcall.dev", token);

await client.PublishFrameAsync(ns, name, opts);
client.SubscribeFrame(ns, name, (tsUs, prio, data) => {...});
client.PublishAudio(...)  / client.SubscribeAudio(...)
client.PublishVideo(...)  / client.SubscribeVideo(...)
client.PublishText(...)   / client.SubscribeText(...)

client.SubscribeNamespace(prefix, (suffix, active) => {...});
client.ConnectionRttUs : ulong
client.MaxDatagramSize : int
await client.DisposeAsync();

Legacy RPC

The root ClutchCall.SDK.ClutchCallClient (Dial, OriginateBulk, Hangup, Barge, PushAudio, …) remains for backwards compat. New code should prefer the voice modality.