The Java SDK ships five modality packages plus a raw MoQT client.
import com.clutchcall.sdk.voice.Voice;
import com.clutchcall.sdk.streams.Streams;
import com.clutchcall.sdk.robotics.Robotics;
import com.clutchcall.sdk.games.Games;
import com.clutchcall.sdk.data.Data;
import com.clutchcall.sdk.Moqt;
Each modality is an AutoCloseable final class with public nested types (Voice.Calls, Voice.Call, Voice.AudioBridge, etc).

Voice

Voice v = new Voice(
    "https://app.clutchcall.dev",       // baseUrl
    System.getenv("CLUTCHCALL_API_KEY"),
    "org_abc"
);

Calls

Voice.Call call = v.calls().originate(new Voice.OriginateArgs()
    .to("+15551234567")
    .from("+15558675309")
    .trunkId("trunk_main")
    .agent("healthcare-assistant"));

v.calls().get(sid);
v.calls().list(orgId, cursor, limit);
v.calls().transfer(sid, new Voice.TransferArgs().to("+1..."));
v.calls().hangup(sid);
v.calls().terminate(sid);
Call exposes:
call.sid()    : String
call.status() : String
call.from()   : String
call.to()     : String

call.refresh()              // → CallData
call.hangup()
call.transfer(args)         // → Call
call.onStatus(consumer)     // → Unsubscribe

AudioBridge

Voice.AudioBridge bridge = v.audioBridge().attach(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);
bridge.close();
Codec: OPUS, PCM16, G711_ULAW, G711_ALAW.

Streams

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

Control plane

Streams.LiveInputWithSecret created =
    s.liveInputs().create("My Show", null /* codecs */, null /* recordingProfile */);
String streamKey = created.streamKey();         // returned ONCE

s.liveInputs().get(id);
s.liveInputs().list(orgId, cursor, limit);
s.liveInputs().rotateStreamKey(id);
s.liveInputs().delete(id);

s.signingKeys().create(orgId, label);
s.signingKeys().list(orgId);
s.signingKeys().retire(id);

s.apiKeys().create(orgId, label, scopes);
s.apiKeys().list(orgId);
s.apiKeys().revoke(id);

s.webhooks().create(orgId, url, events);
s.webhooks().list(orgId);
s.webhooks().delete(id);
s.events().listDeliveries(webhookId, cursor, limit);

s.analytics().viewerMinutes(orgId, from, to, groupBy);
s.analytics().popCache(orgId, from, to);
LiveInput:
input.externalInputId(): String
input.signedPlaybackUrl(ttlSeconds, scopes): Streams.SignedPlaybackUrl

Data plane

The publisher and viewer classes live in Streams.BroadcastPublisher and Streams.BroadcastViewer:
Streams.BroadcastPublisher pub = Streams.BroadcastPublisher.open(
    inputId, streamKey, codecs);
pub.write(chunk);
pub.close(Streams.CloseReason.COMPLETE);

Streams.BroadcastViewer viewer = Streams.BroadcastViewer.open(signedUrl,
    (init, chunk) -> { /* ... */ },
    reason -> { /* ... */ });
viewer.close();

Robotics

Robotics r = new Robotics(
    "relay.clutchcall.dev",
    System.getenv("CLUTCHCALL_RELAY_TOKEN"),
    "turtlebot-7"
);
Robotics.QoSProfile qos = new Robotics.QoSProfile()
    .reliability(Robotics.Reliability.RELIABLE)
    .depth(10);

Robotics.Publication pub = r.publishTelemetry("odom",
    "nav_msgs/msg/Odometry", qos);
pub.write(cdrBytes);
pub.close();

Robotics.Publication cmd = r.publishCommand("cmd_vel",
    "geometry_msgs/msg/Twist", null);

Robotics.Subscription sub = r.subscribeTelemetry("odom", null,
    (payload, typeName) -> { /* ... */ });
sub.close();
Reliability: BEST_EFFORT, RELIABLE. Durability: VOLATILE, TRANSIENT_LOCAL.

Games

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

Player

Games.Subscription stateSub = g.subscribeState(bytes -> render(bytes));
Games.FromPublisher input = g.publishInput();
input.write(serializeInput(localInput));

Games.FromPublisher events = g.publishEvent();
events.send(Map.of("kind", "chat", "text", "gg"));
g.subscribeEvents((evt, fromPlayerId) -> dispatch(evt));

Authority (playerId: null)

Games.StatePublisher state = g.publishState(new Games.StateOpts().tickHz(30));
state.write(serializeState(world));

g.subscribeInputs((playerId, payload) -> applyInput(playerId, payload));
g.subscribeEvents((evt, fromPlayerId) -> { /* ... */ });

Data

Data d = new Data(
    "relay.clutchcall.dev",
    System.getenv("CLUTCHCALL_DATA_TOKEN"),
    "device-7"
);
d.publish("sensors/room1/temperature", "23.5".getBytes(), false, false);

Data.Subscription sub = d.subscribe("sensors/+/temperature",
    msg -> System.out.println(msg.topic() + " ← " + msg.fromClientId() +
                              " = " + new String(msg.payload())));
sub.close();
Data.Message:
msg.topic()        : String
msg.payload()      : byte[]
msg.fromClientId() : String
msg.retain()       : boolean
msg.tsUs()         : long

Realtime Tracks

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

client.publishFrame(namespace, name, opts);
client.subscribeFrame(namespace, name, (tsUs, prio, data) -> {...});
client.publishAudio(...)  / client.subscribeAudio(...)
client.publishVideo(...)  / client.subscribeVideo(...)
client.publishText(...)   / client.subscribeText(...)

client.subscribeNamespace(prefix, (suffix, active) -> {...});
client.connectionRttUs(): long
client.maxDatagramSize(): int
client.close();

Legacy RPC

The root com.clutchcall.sdk.ClutchCallClient (dial, originateBulk, hangup, barge, pushAudio, …) remains for backwards compat. New code should prefer the voice modality.