Enlitt

.NET SDK

Use Enlitt from an ASP.NET Core backend or a Unity dedicated server.

The .NET SDK does what the Node.js SDK does, for an ASP.NET Core server or any .NET 8 backend, such as a Unity dedicated server. It comes as three NuGet packages:

PackageWhat it holds
EnlittEnlittSdk.WithPremiumChargeAsync, EnlittSdk.UsageAsync, sessions, exceptions, and the login handlers.
Enlitt.AspNetCoreUseEnlittSession, MapEnlittAuth, and MapEnlittClientSignIn.
Enlitt.Extensions.AIEnlittChatClient, a Microsoft.Extensions.AI IChatClient for standard and premium calls.

Every rule on the other pages applies unchanged. That includes login, premium limits, idempotency, and errors. This page shows only the .NET names. Errors are exceptions, so EnlittLoginRequiredError becomes EnlittLoginRequiredException.

Install

dotnet add package Enlitt.AspNetCore
dotnet add package Enlitt.Extensions.AI

Then wire the project:

npx enlitt setup --name "My .NET app"

The CLI needs Node.js. setup finds your .csproj or .sln. It then writes ENLITT_CLIENT_ID and ENLITT_SECRET_KEY to .env and records the application in enlitt.json. Keep .env out of git, and load it into the process environment (for example with your host's environment settings or a .env loader). The SDK reads ENLITT_SECRET_KEY from the environment.

Mount login and the session middleware

using Enlitt;
using Enlitt.AspNetCore;
using Enlitt.Auth;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var config = new CallbackConfig
{
    ClientId = Environment.GetEnvironmentVariable("ENLITT_CLIENT_ID")!,
    AppOrigin = "https://your-app.example",
    RedirectUri = "https://your-app.example/enlitt/callback",
    DefaultReturnTo = "/",
    StateStore = new MyStateStore(), // implements IStateStore; share it across instances
    // SecretKey defaults to the ENLITT_SECRET_KEY environment variable.
};

app.UseEnlittSession();
app.MapEnlittAuth(config); // /enlitt/login, /enlitt/callback, /enlitt/logout

MapEnlittAuth takes optional path arguments. The callback path must be the path of RedirectUri. The handlers behave exactly as described on the login page.

Make a standard call

EnlittChatClient.Standard() is an IChatClient, so it works anywhere Microsoft.Extensions.AI does:

using Enlitt.Extensions.AI;
using Microsoft.Extensions.AI;

IChatClient standard = EnlittChatClient.Standard();

app.MapPost("/api/summary", async (CancellationToken ct) =>
{
    ChatResponse response = await standard.GetResponseAsync("Summarize this support ticket.", cancellationToken: ct);
    return Results.Json(new { text = response.Text });
});

GetStreamingResponseAsync streams. Cancel a call with the CancellationToken. The standard model's limits are on the models page; unsupported ChatOptions, such as Temperature or a required tool mode, throw EnlittConfigurationException before any request is sent.

Make a premium call

Wrap your own provider's IChatClient. Your provider key stays on your server:

IChatClient premium = EnlittChatClient.Premium(myProviderChatClient);

var options = new ChatOptions
{
    AdditionalProperties = new()
    {
        ["enlitt"] = new EnlittProviderOptions
        {
            MaxCharge = "20000000", // NANOUSD, as a string
            Price = EnlittPricing.PerToken(input: "400", output: "1600"),
        },
    },
};

ChatResponse response = await premium.GetResponseAsync("Write a long story.", options, ct);

The provider and model labels come from the wrapped client's ChatClientMetadata.

For any other provider (images, voice, another SDK), use WithPremiumChargeAsync:

byte[] image = await EnlittSdk.WithPremiumChargeAsync(
    new WithPremiumChargeOptions
    {
        MaxCharge = "5000000",
        Provider = new PremiumProvider("my-image-provider", "image-v2"),
    },
    async (charge, operation, ct) =>
    {
        byte[] result = await myImageProvider.GenerateAsync(prompt, ct);
        charge("5000000"); // what this call costs the user, in NANOUSD
        return result;
    },
    ct);

The call is reported exactly once. If the delegate returns, it is reported as completed; if it throws, as failed; if its token is cancelled, as cancelled.

Remaining capacity

var capacity = await EnlittSdk.UsageAsync();
Console.WriteLine(capacity.Standard.RemainingPercent);

Exceptions

The classes are:

  • EnlittException
  • EnlittLoginRequiredException
  • EnlittInsufficientCreditsException
  • EnlittLimitExceededException
  • EnlittSubscriptionInactiveException
  • EnlittRateLimitException
  • EnlittAuthenticationException
  • EnlittAuthorizationException
  • EnlittConfigurationException

Each carries Code, ProviderInvoked, Retryable, RetryAfterMs, RequestId, and AuthorizeUrl. The errors page says which ones are safe to show a user.

try
{
    response = await standard.GetResponseAsync(prompt, cancellationToken: ct);
}
catch (EnlittLoginRequiredException)
{
    return Results.Redirect("/enlitt/login?return_to=/chat");
}

Background jobs

Outside a request, wrap the work in a session you stored:

await EnlittSdk.RunWithEnlittSessionAsync(new EnlittSession(storedToken), async () =>
{
    await standard.GetResponseAsync("Write tonight's digest.");
});

Games and native apps

If your server backs a Unity game or a desktop or mobile app, add app.MapEnlittClientSignIn(...) and app.UseEnlittSession(new EnlittSessionMiddlewareOptions { Header = true }). See client sign-in.

On this page