Skip to content

Inworld

Modern .NET SDK for Inworld generated from the provider's OpenAPI definition with AutoSDK.

Nuget package dotnet License: MIT Discord

Generated from the source spec

Built from Inworld's OpenAPI definition so the SDK stays close to the upstream API surface.

Auto-updated

Designed for fast regeneration and low-friction updates when the upstream API changes without breaking compatibility.

Modern .NET

Targets current .NET practices including nullability, trimming, NativeAOT awareness, and source-generated serialization.

Docs from examples

Examples stay in sync between the README, MkDocs site, and integration tests through the AutoSDK docs pipeline.

Usage

1
2
3
using Inworld;

using var client = new InworldClient(apiKey);

Text to Speech

Synthesize natural-sounding speech from text using an Inworld voice.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Create an Inworld client using your API key.
using var client = new InworldClient(apiKey);

// Synthesize a short greeting using the Inworld TTS 1.5 Max model.
var response = await client.TextToSpeech.SynthesizeSpeechAsync(
    text: "Hello, welcome to Inworld.",
    voiceId: "Dennis",
    modelId: "inworld-tts-1.5-max");

// The response contains Base64-encoded audio bytes ready to decode into a playable file.

List Voices

Discover voices available to your Inworld workspace.

1
2
3
4
using var client = new InworldClient(apiKey);

// List all voices in the workspace. Pass the `languages` parameter to filter by language code.
var response = await client.Voices.ListVoicesAsync();

List Models

Inworld's LLM Router exposes models from multiple providers. Use this call to discover which ones are available with their capabilities and pricing.

1
2
3
4
using var client = new InworldClient(apiKey);

// Fetch all supported LLM models along with their modalities, context length, and pricing.
var response = await client.Models.ListModelsAsync();

MEAI Tools

Using Inworld endpoints as AIFunction tools with any Microsoft.Extensions.AI IChatClient.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
using var client = new InworldClient(apiKey);

// Create AIFunction tools from the Inworld client.
var synthesizeTool = client.AsSynthesizeSpeechTool();
var listVoicesTool = client.AsListVoicesTool();
var listModelsTool = client.AsListModelsTool();
var designVoiceTool = client.AsDesignVoiceTool();

// Verify all tools are created with the expected names.

// These tools can be passed to any IChatClient for function calling.
var tools = new[] { synthesizeTool, listVoicesTool, listModelsTool, designVoiceTool };

JWT Auth (Client-Side)

For client-side use (Blazor WebAssembly, browser, mobile) you should never embed the long-lived Basic API key. Instead, exchange it for a short-lived JWT on a trusted backend via InworldJwt.GenerateAsync(key, secret) and pass the JWT to the client. The SDK auto-detects JWTs (Bearer) and skips the Basic rewrite.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Read the Inworld API key/secret from environment. The Basic key
// is Base64 of `key:secret`; for client-side usage we decode it into
// the pair before calling token:generate.
var apiKey =
    Environment.GetEnvironmentVariable("INWORLD_JWT_KEY") is { Length: > 0 } k ? k :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;

var apiSecret =
    Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET") is { Length: > 0 } s ? s :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;

if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret))
{
    throw new AssertInconclusiveException("INWORLD_API_KEY (Basic Base64) or INWORLD_JWT_KEY + INWORLD_JWT_SECRET is required.");
}

// Mint a short-lived Bearer JWT from the key+secret pair.
var token = await InworldJwt.GenerateAsync(apiKey, apiSecret);

// The token is ready to pass to `new InworldClient(token.Token)` on the client.

// Use the JWT to call a REST endpoint. The SDK keeps the Bearer scheme for JWTs.
using var client = new InworldClient(token.Token);
var models = await client.Models.ListModelsAsync();

JWT Cache

InworldJwtCache keeps one JWT per (apiKey, resources) pair in process and refreshes it ~60s before expirationTime. Use it on Blazor/ASP.NET Core backends to avoid hitting /auth/v1/tokens/token:generate on every request.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var apiKey =
    Environment.GetEnvironmentVariable("INWORLD_JWT_KEY") is { Length: > 0 } k ? k :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;
var apiSecret =
    Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET") is { Length: > 0 } s ? s :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;

if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret))
{
    throw new AssertInconclusiveException("INWORLD_API_KEY (or INWORLD_JWT_KEY + INWORLD_JWT_SECRET) is required.");
}

// Create one cache per (apiKey, resources) and reuse it across requests.
using var cache = new InworldJwtCache(apiKey, apiSecret);

// First call mints a fresh JWT.
var first = await cache.GetAsync();

// Second call returns the same cached token (no second round trip).
var second = await cache.GetAsync();

// After invalidate the cache mints a new token.
cache.Invalidate();
var third = await cache.GetAsync();
// Inworld may issue identical JWTs within the same clock second — don't
// rely on the token value changing; just verify we got one back.

MEAI ISpeechToTextClient

InworldClient implements Microsoft.Extensions.AI.ISpeechToTextClient so the same call site works with Inworld, Deepgram, or any other MEAI STT provider.

Non-streaming calls hit the REST /stt/v1/transcribe endpoint. Streaming calls open a WebSocket to /stt/v1/transcribe:streamBidirectional.

1
2
3
4
5
6
7
8
using var client = new InworldClient(apiKey);

// InworldClient implements Meai.ISpeechToTextClient directly.
Meai.ISpeechToTextClient speechClient = client;

// Metadata is exposed via ISpeechToTextClient.GetService.
var metadata = speechClient.GetService(typeof(Meai.SpeechToTextClientMetadata))
    as Meai.SpeechToTextClientMetadata;

JwtCache + Streaming STT

Wire an InworldJwtCache into InworldClient so the realtime (WebSocket) streaming path pulls a fresh JWT from the cache on every connect. This keeps long-lived backends correct across token rotations without reconstructing the client.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var apiKey =
    Environment.GetEnvironmentVariable("INWORLD_JWT_KEY") is { Length: > 0 } k ? k :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;
var apiSecret =
    Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET") is { Length: > 0 } s ? s :
    DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;

if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(apiSecret))
{
    throw new AssertInconclusiveException("INWORLD_API_KEY (or INWORLD_JWT_KEY + INWORLD_JWT_SECRET) is required.");
}

// Build a JwtCache once — in production it would be a singleton.
using var jwtCache = new InworldJwtCache(apiKey, apiSecret);

// Construct InworldClient directly from the cache: REST uses the
// token active at construction, streaming fetches a fresh JWT per
// connect via RealtimeTokenProvider.
using var client = new InworldClient(jwtCache);

// Prove the provider hook is wired up.
var preview = await client.RealtimeTokenProvider!(CancellationToken.None);

// Synthesize a short phrase so we have something for STT to hear.
const string phrase = "JWT cache is wired into streaming.";
var tts = await client.TextToSpeech.SynthesizeSpeechAsync(
    text: phrase,
    voiceId: "Dennis",
    modelId: "inworld-tts-1.5-max",
    audioConfig: new AudioConfig
    {
        AudioEncoding = AudioEncoding.Linear16,
        SampleRateHertz = 16000,
    });

var audio = StripWavHeader(tts.AudioContent!);

// Stream it back through MEAI. The WebSocket connect pulls a fresh
// token from the cache under the hood.
Meai.ISpeechToTextClient speech = client;
using var audioStream = new MemoryStream(audio);

var transcriptBuilder = new System.Text.StringBuilder();
await foreach (var update in speech.GetStreamingTextAsync(
    audioStream,
    new Meai.SpeechToTextOptions
    {
        ModelId = "assemblyai/universal-streaming-multilingual",
        SpeechLanguage = "en-US",
    },
    CancellationToken.None))
{
    if (update.Kind == Meai.SpeechToTextResponseUpdateKind.TextUpdated)
    {
        transcriptBuilder.Append(update.Text);
    }

    if (update.Kind == Meai.SpeechToTextResponseUpdateKind.SessionClose)
    {
        break;
    }
}

    because: "streaming STT via JwtCache-backed auth should still return a transcript");

Support

Bugs

Open an issue in tryAGI/Inworld.

Ideas and questions

Use GitHub Discussions for design questions and usage help.

Community

Join the tryAGI Discord for broader discussion across SDKs.

Acknowledgments

JetBrains logo

This project is supported by JetBrains through the Open Source Support Program.