Synthesize natural-sounding speech from text using an Inworld voice.
1 2 3 4 5 6 7 8 910
// Create an Inworld client using your API key.usingvarclient=newInworldClient(apiKey);// Synthesize a short greeting using the Inworld TTS 1.5 Max model.varresponse=awaitclient.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.
1234
usingvarclient=newInworldClient(apiKey);// List all voices in the workspace. Pass the `languages` parameter to filter by language code.varresponse=awaitclient.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.
1234
usingvarclient=newInworldClient(apiKey);// Fetch all supported LLM models along with their modalities, context length, and pricing.varresponse=awaitclient.Models.ListModelsAsync();
MEAI Tools
Using Inworld endpoints as AIFunction tools with any Microsoft.Extensions.AI IChatClient.
1 2 3 4 5 6 7 8 9101112
usingvarclient=newInworldClient(apiKey);// Create AIFunction tools from the Inworld client.varsynthesizeTool=client.AsSynthesizeSpeechTool();varlistVoicesTool=client.AsListVoicesTool();varlistModelsTool=client.AsListModelsTool();vardesignVoiceTool=client.AsDesignVoiceTool();// Verify all tools are created with the expected names.// These tools can be passed to any IChatClient for function calling.vartools=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 9101112131415161718192021222324
// 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.varapiKey=Environment.GetEnvironmentVariable("INWORLD_JWT_KEY")is{Length:>0}k?k:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;varapiSecret=Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET")is{Length:>0}s?s:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;if(string.IsNullOrEmpty(apiKey)||string.IsNullOrEmpty(apiSecret)){thrownewAssertInconclusiveException("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.vartoken=awaitInworldJwt.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.usingvarclient=newInworldClient(token.Token);varmodels=awaitclient.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.
varapiKey=Environment.GetEnvironmentVariable("INWORLD_JWT_KEY")is{Length:>0}k?k:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;varapiSecret=Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET")is{Length:>0}s?s:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;if(string.IsNullOrEmpty(apiKey)||string.IsNullOrEmpty(apiSecret)){thrownewAssertInconclusiveException("INWORLD_API_KEY (or INWORLD_JWT_KEY + INWORLD_JWT_SECRET) is required.");}// Create one cache per (apiKey, resources) and reuse it across requests.usingvarcache=newInworldJwtCache(apiKey,apiSecret);// First call mints a fresh JWT.varfirst=awaitcache.GetAsync();// Second call returns the same cached token (no second round trip).varsecond=awaitcache.GetAsync();// After invalidate the cache mints a new token.cache.Invalidate();varthird=awaitcache.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.
12345678
usingvarclient=newInworldClient(apiKey);// InworldClient implements Meai.ISpeechToTextClient directly.Meai.ISpeechToTextClientspeechClient=client;// Metadata is exposed via ISpeechToTextClient.GetService.varmetadata=speechClient.GetService(typeof(Meai.SpeechToTextClientMetadata))asMeai.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.
varapiKey=Environment.GetEnvironmentVariable("INWORLD_JWT_KEY")is{Length:>0}k?k:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).key;varapiSecret=Environment.GetEnvironmentVariable("INWORLD_JWT_SECRET")is{Length:>0}s?s:DecodeKeyPair(Environment.GetEnvironmentVariable("INWORLD_API_KEY")).secret;if(string.IsNullOrEmpty(apiKey)||string.IsNullOrEmpty(apiSecret)){thrownewAssertInconclusiveException("INWORLD_API_KEY (or INWORLD_JWT_KEY + INWORLD_JWT_SECRET) is required.");}// Build a JwtCache once — in production it would be a singleton.usingvarjwtCache=newInworldJwtCache(apiKey,apiSecret);// Construct InworldClient directly from the cache: REST uses the// token active at construction, streaming fetches a fresh JWT per// connect via RealtimeTokenProvider.usingvarclient=newInworldClient(jwtCache);// Prove the provider hook is wired up.varpreview=awaitclient.RealtimeTokenProvider!(CancellationToken.None);// Synthesize a short phrase so we have something for STT to hear.conststringphrase="JWT cache is wired into streaming.";vartts=awaitclient.TextToSpeech.SynthesizeSpeechAsync(text:phrase,voiceId:"Dennis",modelId:"inworld-tts-1.5-max",audioConfig:newAudioConfig{AudioEncoding=AudioEncoding.Linear16,SampleRateHertz=16000,});varaudio=StripWavHeader(tts.AudioContent!);// Stream it back through MEAI. The WebSocket connect pulls a fresh// token from the cache under the hood.Meai.ISpeechToTextClientspeech=client;usingvaraudioStream=newMemoryStream(audio);vartranscriptBuilder=newSystem.Text.StringBuilder();awaitforeach(varupdateinspeech.GetStreamingTextAsync(audioStream,newMeai.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");