Hello, dial

using TeleQuick.SDK;

var client = new TeleQuickClient(
    "quic://engine.telequick.dev:9090",
    "/etc/telequick/service-account.json"
);

await client.DialAsync(
    to: "sip:+15551234567@example.sip.livekit.cloud",
    trunkId: "default",
    callFrom: "+18005550100",
    maxDurationMs: 60_000
);

await Task.Delay(TimeSpan.FromSeconds(30));
DialAsync lazily opens the QUIC connection, signs the JWT, and subscribes events. After this returns the gateway is actively routing your call.

Receiving audio and events

client.OnAudioFrame = payload =>
{{
    // payload is the AudioFrame envelope. Decode using the FFI helpers.
    Console.WriteLine($"audio: {{payload.Length}} bytes");
}};

client.OnCallEvent = payload =>
{{
    // CallEvent envelope. Decode similarly.
    Console.WriteLine($"event: {{payload.Length}} bytes");
}};
Set the callbacks before the first RPC.

Pushing audio

var pcm = await File.ReadAllBytesAsync("./prompt.alaw");
const int frame = 160;  // 20 ms PCMU
ulong seq = 0;

for (int i = 0; i < pcm.Length; i += frame)
{{
    var len = Math.Min(frame, pcm.Length - i);
    var slice = new byte[len];
    Array.Copy(pcm, i, slice, 0, len);
    await client.PushAudioAsync(callSid, slice, "PCMU", seq++, false);
    await Task.Delay(20);
}}

Hanging up

await client.TerminateAsync(callSid);

Cancellation

The current API does not accept CancellationTokens. To bound long-running RPCs, race the task against a delay:
var dial = client.DialAsync(to, trunkId);
var timeout = Task.Delay(TimeSpan.FromSeconds(5));
if (await Task.WhenAny(dial, timeout) == timeout)
    Console.WriteLine("dial timed out");
Note that the underlying QUIC write may still complete after the timeout.

Keep the connection open

If you create the client at app startup and never call any RPC, the QUIC session is not opened until the first call. To open eagerly:
await client.StreamEventsAsync(Guid.NewGuid().ToString());
This forces a connect and re-subscribes events under a custom client_id — side effect: incoming events are now routed to that id, not the SDK’s default one. For most apps the lazy-connect on first DialAsync is fine.