API reference

September 17, 2026 ยท View on GitHub

This page is the compact reference for the public C# surface. It is maintained alongside the implementation so documentation tools do not have to infer behaviour from API-compatibility baseline files.

For task-oriented examples, start with the quickstart. For the complete semantics of a specific area, follow the links in each section below.

Packages and namespaces

PackageNamespacePurpose
TypeSafe.SdkTypeSafeClient, questions, answers, retries, errors, models, and JSON helpers.
TypeSafe.SdkTypeSafe.SerializationTypeSafeJson and the public JSON converters.
TypeSafe.Sdk.DependencyInjectionTypeSafe.DependencyInjectionIServiceCollection and IHttpClientFactory integration.

Both packages target .NET 8 and .NET 10. The client is asynchronous, thread-safe, and intended to be reused.

Create a client

using TypeSafe;

// Resolves the API key from TYPESAFE_API_KEY.
using var client = new TypeSafeClient();

// An explicit key takes precedence over options and the environment.
using var explicitClient = new TypeSafeClient("ts_...");

// Configure the client directly.
using var configuredClient = new TypeSafeClient(new TypeSafeClientOptions
{
    ApiKey = "ts_...",
    BaseUrl = new Uri("https://api.typesafe.ai"),
    Model = "jev-latest",
    Timeout = TimeSpan.FromSeconds(10),
    Retry = RetryPolicy.Default,
});

The public constructors are:

TypeSafeClient(TypeSafeClientOptions? options = null, HttpClient? httpClient = null)
TypeSafeClient(string apiKey, TypeSafeClientOptions? options = null, HttpClient? httpClient = null)

When the SDK creates the HttpClient, it owns and disposes it. A caller-supplied HttpClient remains owned by the caller, keeps its caller-controlled settings, and is not disposed by TypeSafeClient.

TypeSafeClientOptions

PropertyTypeDefault or source
ApiKeystring?TYPESAFE_API_KEY; required when the client is constructed.
BaseUrlUri?TYPESAFE_BASE_URL, then TYPESAFE_ENDPOINT, then https://api.typesafe.ai.
Modelstring?TYPESAFE_DEFAULT_MODEL, then jev-latest.
TimeoutTimeSpan10 seconds per HTTP attempt.
RetryRetryPolicyRetryPolicy.Default.
DefaultHeadersIReadOnlyDictionary<string, string>?No extra headers.
UserAgentstring?Appended to the SDK user-agent token when supplied.
LoggerFactoryILoggerFactory?No SDK logging when omitted.
TimeProviderTimeProvider?TimeProvider.System; injectable for testing.

BaseUrl is the API root and must not include a version segment. The SDK appends /v1/systemone and /v1/models itself.

Submit a System One request

ITypeSafeClient and TypeSafeClient expose the same five overloads:

Task<SystemOneResult> SystemOneAsync(
    SystemOneRequest request,
    CancellationToken cancellationToken = default)

Task<SystemOneResult> SystemOneAsync(
    string state,
    IEnumerable<Question> questions,
    CancellationToken cancellationToken = default)

Task<SystemOneResult> SystemOneAsync(
    JsonNode? state,
    IEnumerable<Question> questions,
    CancellationToken cancellationToken = default)

Task<SystemOneResult> SystemOneAsync<TState>(
    TState state,
    IEnumerable<Question> questions,
    JsonTypeInfo<TState> stateTypeInfo,
    CancellationToken cancellationToken = default)

Task<SystemOneResult> SystemOneAsync<TState>(
    TState state,
    IEnumerable<Question> questions,
    CancellationToken cancellationToken = default)

Use the JsonTypeInfo<TState> overload for trimming and Native AOT. The final generic overload uses reflection and is annotated accordingly. A state must not be null, and every request must contain at least one question with a unique id.

SystemOneRequest

PropertyTypeMeaning
StateJsonNode?Required text, object, or array to evaluate. The value itself must not be null.
QuestionsIEnumerable<Question>Required non-empty collection with unique ids.
Modelstring?Model for this request, or the client default.
OptionsTypeSafeRequestOptions?Per-call retry, timeout, header, model, and body overrides.
AdditionalPropertiesIReadOnlyDictionary<string, JsonNode?>?Extra top-level request fields, merged last.

TypeSafeRequestOptions

PropertyTypeMeaning
Modelstring?Overrides the client model for one call. SystemOneRequest.Model wins when both are set.
RetryRetryPolicy?Overrides the client retry policy. Use RetryPolicy.None to disable retries.
TimeoutTimeSpan?Overrides the per-attempt timeout.
HeadersIReadOnlyDictionary<string, string>?Merged over the client default headers.
AdditionalBodyPropertiesIReadOnlyDictionary<string, JsonNode?>?Extra top-level fields, merged last.

See Forward compatibility before overriding a modelled field through an additional-properties dictionary.

Questions

All question ids are local map keys. They are not sent inside the individual question body.

TypeConstructor shapeAnswer
NoulQuestion(id, instructions?, criteria?, additionalProperties?)NoulAnswer
ChoiceQuestion(id, instructions, labels, additionalProperties?) or (id, instructions, criteria, additionalProperties?)ChoiceAnswer
ScoreQuestion(id, instructions, levels, additionalProperties?)ScoreAnswer
RawQuestion(id, type, body)A known answer type or UnknownAnswer

QuestionSet is an ordered collection that rejects duplicate ids when they are added. The client also accepts any IEnumerable<Question> and validates it before making a network call.

See Questions for criteria shapes, limits, and complete examples.

Results and answers

SystemOneResult exposes all answers through Answers and typed views through Nouls, Choices, Scores, and UnknownAnswers.

MemberMeaning
Get(id)Return any answer, or throw when the id is absent.
Get(question)Return the answer type bound to an IQuestion<TAnswer>.
TryGet(id, out answer)Non-throwing lookup by id.
TryGet(question, out answer)Non-throwing, strongly typed lookup.
Noul(id), Choice(id), Score(id)Return a specific answer kind.
Contains(id), IdsInspect which answers were returned.
ModelConcrete model reported by the API.
UsageInput, output, and total token counts when reported.
RequestIdValue of the x-typesafe-request-id response header.
RawJsonComplete response body, including fields not modelled by this SDK version.

Answer types

TypeImportant members
NoulAnswerProbability; a noul deliberately has no confidence property.
ChoiceAnswerLabel, Confidence, Probabilities, TopLabel, TopProbability, Ranked(), Top(n), ProbabilityOf(label), NormalizedEntropy().
ScoreAnswerScore, NormalizedScore, ExpectedLevel, Confidence, Variance, Legend, Probabilities, ProbabilityAtLevel(level), NormalizedEntropy().
UnknownAnswerType and the complete answer object in Raw.

Every answer also carries Id, Type, AdditionalProperties, and TryGetConfidence(out double). TryGetConfidence returns false for answer kinds that do not report confidence.

See Answers and confidence for distribution semantics and threshold guidance.

List models

var result = await client.Models.ListAsync();

foreach (var model in result.Models)
{
    Console.WriteLine($"{model.Name} ({model.ReleaseDate}): {model.Description}");
}

ModelsResult also exposes RequestId and RawJson.

Retries and errors

RetryPolicy.Default makes two retries after the initial attempt. It retries HTTP 408, 429, and 500 through 599, plus connection and per-attempt timeout failures. The first backoff is 500 milliseconds, the maximum backoff is 5 seconds, and the default retry-admission budget is 30 seconds.

RetryAttempt.Attempt is one-based: attempt 1 is the initial request that failed. Delay is the wait before the next attempt, and Exception is the failure that triggered the retry.

Every SDK exception derives from TypeSafeException. HTTP failures derive from TypeSafeApiException; connection failures use TypeSafeConnectionException, and per-attempt timeouts use TypeSafeTimeoutException. A caller cancellation remains an OperationCanceledException.

See Retries and errors for the complete policy defaults, exception table, and catch ordering.

Dependency injection

Install TypeSafe.Sdk.DependencyInjection, then register both the concrete client and interface:

using TypeSafe.DependencyInjection;

builder.Services.AddTypeSafeClient(builder.Configuration);

The overloads are:

IHttpClientBuilder AddTypeSafeClient(
    this IServiceCollection services,
    Action<TypeSafeClientOptions>? configure = null)

IHttpClientBuilder AddTypeSafeClient(
    this IServiceCollection services,
    IConfiguration configuration,
    Action<TypeSafeClientOptions>? configure = null)

The configuration overload binds the TypeSafe section. The returned IHttpClientBuilder can add handlers or change the named TypeSafe client. The SDK remains responsible for retries; adding a second retry handler can multiply the total number of attempts.

HTTP wire contract

Normal applications should use TypeSafeClient rather than constructing HTTP requests directly. The wire examples here document what the SDK sends and prevent tooling from inventing endpoint or payload shapes.

System One requests use POST /v1/systemone. questions is an object keyed by question id, not an array:

{
  "state": "Help! My payouts have been failing for 3 days.",
  "model": "jev-latest",
  "questions": {
    "is_urgent": {
      "type": "noul",
      "instructions": "Does this convey urgency?"
    }
  }
}

The response is the result object itself. It is not wrapped in a result property:

{
  "model": "jev-latest",
  "answers": {
    "is_urgent": {
      "type": "noul",
      "noul": 0.92
    }
  },
  "usage": {
    "input_tokens": 312,
    "output_tokens": 48
  }
}

The SDK sends the API key as a bearer token and records the optional x-typesafe-request-id response header on results and API exceptions.