SDKs & toolsC# / .NET SDK

C# / .NET SDK

⚠️
Preview — this feature is being built; the design below is the spec.

Syntic on NuGet is the planned official .NET client. It is generated from the OpenAPI description, uses async methods throughout, works with dependency injection, and adds helpers for streaming with IAsyncEnumerable, tool loops, pagination and batches. It is in the second release wave; see Other languages.

Today, use HttpClient as shown in Quickstart: C#. That code keeps working after the SDK ships.

Install

dotnet add package Syntic
RequirementValue
.NET8.0 or newer
PackageSyntic on NuGet
DependenciesSystem.Text.Json, Microsoft.Extensions.Http (optional, for DI)

Create a client

using Syntic;
 
var client = new SynticClient(); // reads SYNTIC_API_KEY and SYNTIC_BASE_URL
 
var custom = new SynticClient(new SynticClientOptions
{
    ApiKey = Environment.GetEnvironmentVariable("SYNTIC_API_KEY"),
    BaseUrl = new Uri("https://app.syntic.ai/v1"),
    MaxRetries = 3,
    Timeout = TimeSpan.FromMinutes(5),
});

With dependency injection:

builder.Services.AddSyntic(options => options.MaxRetries = 3); // registers SynticClient via IHttpClientFactory
OptionDefaultNotes
ApiKeySYNTIC_API_KEYPrivate key or workload identity token.
BaseUrlSYNTIC_BASE_URL, else https://app.syntic.ai/v1
VersionThe release’s API versionSent as syntic-version.
BetasemptySent as syntic-beta; methods that need a flag add it.
MaxRetries2rate_limit, overloaded, api_error and transient network errors.
Timeout10 minutes

A first request

var reply = await client.Chat.Completions.CreateAsync(new()
{
    Model = "Amara S1.2",
    MaxTokens = 200,
    Messages = [Message.System("Answer in one sentence."), Message.User("Why is the sky blue?")],
});
 
Console.WriteLine(reply.Choices[0].Message.Content);
Console.WriteLine($"{reply.Usage.TotalTokens} tokens, request {reply.RequestId}");

Streaming

await using var stream = client.Chat.Completions.Stream(new()
{
    Model = "Amara Quick",
    Messages = [Message.User("Write a haiku about rain.")],
});
 
await foreach (var text in stream.TextDeltasAsync(cancellationToken))
    Console.Write(text);
 
var final = await stream.GetFinalCompletionAsync(); // assembled message, tool calls, usage

Cancelling the token or disposing the stream closes the connection.

Tool loop

var getOrder = Tool.Create("get_order", "Look up an order by its id.",
    ([Description("Order id such as 88123")] string orderId) =>
        new { order_id = orderId, status = "shipped" });
 
var result = await client.Tools.RunAsync(new()
{
    Model = "Amara S1.2",
    Messages = [Message.User("Where is order 88123?")],
    Tools = [getOrder],
    MaxRounds = 8,
});
Console.WriteLine(result.FinalMessage.Content);

Errors

Exceptionerror.type
SynticInvalidRequestExceptioninvalid_request
SynticAuthenticationExceptionauthentication
SynticPermissionExceptionpermission
SynticNotFoundExceptionnot_found
SynticQuotaExhaustedExceptionquota_exhausted
SynticRateLimitExceptionrate_limit
SynticOverloadedExceptionoverloaded
SynticServerExceptionapi_error

All derive from SynticApiException with StatusCode, Type, Code, Param, Details and RequestId. Network failures surface as HttpRequestException or TaskCanceledException.

try
{
    await client.Chat.Completions.CreateAsync(request);
}
catch (SynticQuotaExhaustedException e)
{
    Console.WriteLine($"Paused until {e.Details["resets_at"]}");
}

Pagination

await foreach (var agent in client.Agents.ListAutoPagingAsync(new() { Limit = 50 }))
    Console.WriteLine($"{agent.Id} {agent.Name}");

Files, batches, agents and admin

var file = await client.Files.UploadAsync("contract.pdf", purpose: "document");
 
var batch = await client.Batches.CreateAsync(new() { InputFileId = file.Id });
batch = await client.Batches.WaitAsync(batch.Id, pollInterval: TimeSpan.FromSeconds(30));
 
var session = await client.Sessions.CreateAsync(new() { AgentId = "agt_01JC4R8W6Q2N9X3T5V7Y" });
await foreach (var ev in client.Sessions.StreamAsync(session.Id))
    if (ev.Type == "message.completed") Console.WriteLine(ev.Content);
 
var admin = new SynticAdminClient(); // reads SYNTIC_ADMIN_KEY
await foreach (var user in admin.Users.ListAutoPagingAsync())
    Console.WriteLine(user.Email);

Raw responses

var raw = await client.Chat.Completions.WithRawResponse.CreateAsync(request);
Console.WriteLine(raw.Headers.GetValues("x-syntic-billing-source").First());
var reply = raw.Parse();
Was this page helpful?