KeyInteractive.Common.Api 2.3.0

dotnet add package KeyInteractive.Common.Api --version 2.3.0
                    
NuGet\Install-Package KeyInteractive.Common.Api -Version 2.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KeyInteractive.Common.Api" Version="2.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KeyInteractive.Common.Api" Version="2.3.0" />
                    
Directory.Packages.props
<PackageReference Include="KeyInteractive.Common.Api" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KeyInteractive.Common.Api --version 2.3.0
                    
#r "nuget: KeyInteractive.Common.Api, 2.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KeyInteractive.Common.Api@2.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KeyInteractive.Common.Api&version=2.3.0
                    
Install as a Cake Addin
#tool nuget:?package=KeyInteractive.Common.Api&version=2.3.0
                    
Install as a Cake Tool

Key Interactive.Common.Api

KeyInteractive.Common.Api is the generic plumbing to build .NET clients for Key Interactive-style APIs: a standard response envelope, an idempotent-only retry policy with exponential backoff, JWT access + rotating refresh token handling, and the conventional Key Interactive request headers.

It ships no endpoints of its own — it's the base your own API consumer inherits from. For the official client of the Key Interactive Users API, see KeyInteractive.Common.Api.Users.

What's in it

  • KiApiResponse / KiApiResponseData — the standard response envelope (Success, ResponseCode, ErrorMessage, optionally Data). Every consumer method built on this package returns one of these — callers branch on Success, never on a caught exception: network/HTTP/JSON failures are turned into a synthetic error response, never thrown.
  • ITokenProvider / TokenProvider — holds the JWT access + refresh token for one consumer instance. Register it Scoped in Blazor Server (one instance per circuit); never make an implementation of ITokenProvider static, or tokens leak between users of the same process after a SignalR reconnect. Also holds the storage name (SetStorageName/ GetStorageName) this circuit uses for its session — see "Rotated refresh token storage" below.
  • KiApiConsumerOptions — per-instance configuration (base URI, optional API key/application key, API version segment, token storage key prefix, device info, timeout).
  • IKiApiConsumer — the minimal interface every consumer implements (SetSiteBaseUrl/SetSiteLanguage).
  • KiApiConsumerBase — the abstract base class. Handles the HttpClient lifecycle, retry, token rotation, and conventional headers; exposes GetAsync/PostAsync/PutAsync/DeleteAsync helpers so each endpoint in your derived consumer is a one-liner.

Requirements

Targets .NET 8.0 or later (net8.0, net10.0). Microsoft.JSInterop is referenced per-TFM (8.0.x on net8.0, 10.0.x on net10.0) and resolved automatically by NuGet.

Installation

dotnet add package KeyInteractive.Common.Api

Building your own API consumer

using KeyInteractive.Common.Api;

public class MyApiConsumer : KiApiConsumerBase, IMyApiConsumer
{
    public MyApiConsumer(HttpClient? httpClient = null, ITokenProvider? tokenProvider = null, IJSRuntime? jsRuntime = null)
        : base(httpClient, new KiApiConsumerOptions
        {
            BaseUri = "https://my-api.example.com/",
            ApiKey = "your-api-key",           // optional: omit (leave null) to skip the header entirely
            ApplicationKey = "your-app-id",     // optional, same as above
            ApiVersion = "v1",                  // path segment, defaults to "v1"
            StorageKeyPrefix = "myapi",         // must be unique per consumer in the same site
        }, tokenProvider, jsRuntime)
    {
    }

    public Task<APIThingResponse> GetThing(int id) =>
        GetAsync<APIThingResponse>($"Thing/{id}");

    // Not idempotent by default: safe against double-submission on retry.
    public Task<KiApiResponse> DoStuff(StuffDto dto) =>
        PostAsync<KiApiResponse>("Thing/DoStuff", dto);

    // Pass isIdempotent: true only for endpoints that are genuinely safe to retry
    // automatically (e.g. a read-only server-to-server lookup implemented as a POST).
    public Task<APIThingResponse> LookupThing(LookupDto dto) =>
        PostAsync<APIThingResponse>("Thing/Lookup", dto, isIdempotent: true);
}

APIThingResponse is any class deriving from KiApiResponse (or KiApiResponseData if you need a generic payload wrapper).

Response envelope

Every call returns a KiApiResponse-derived type — check Success, never rely on an exception:

var result = await myApiConsumer.GetThing(42);

if (result.Success)
{
    // use result.Thing (or whatever fields your APIThingResponse adds)
}
else
{
    Console.WriteLine(result.ErrorMessage);
}

Dependency injection (Blazor Server)

// Program.cs
builder.Services.AddScoped<ITokenProvider, TokenProvider>();
builder.Services.AddHttpClient<IMyApiConsumer, MyApiConsumer>();

If a single site talks to two different APIs built on this package (e.g. the Users API plus your own), register two distinct typed HttpClients and give each KiApiConsumerOptions its own StorageKeyPrefix — otherwise both consumers would fight over the same {prefix}_auth_token/{prefix}_auth_refresh_token storage keys. If the two APIs also have independent login sessions, register two separate ITokenProvider implementations too (e.g. via keyed services or two marker interfaces), rather than sharing one.

Behavior you can rely on

  • Retry: automatic only for idempotent calls (GetAsync, or PostAsync/PutAsync/DeleteAsync explicitly marked isIdempotent: true), with exponential backoff, and only on 408/429/502/503/504 or a network-level HttpRequestException. Non-idempotent POSTs fail on the first error — retrying after the server already processed the request would risk duplicating its effect.
  • Refresh token rotation: applied from the response headers before branching on success/error, since the API may rotate the refresh token on a request it still rejects (e.g. wrong password). Optionally persisted to localStorage/sessionStorage via IJSRuntime, if one is provided.
  • Rotated refresh token storage: persisted to whichever storage TokenProvider.GetStorageName() reports for the current circuit — call tokenProvider.SetStorageName("localStorage") or "sessionStorage" once, at login or at session bootstrap, based on your own "remember me" flag. Never re-derive it by probing localStorage for an existing token: localStorage is shared by every tab of the same browser/domain, so a tab that only uses sessionStorage could find another tab's (another user's, another tenant's) localStorage session and overwrite it with its own rotated token — a cross-tenant session leak. If SetStorageName is never called, the fallback is always "sessionStorage" (never "localStorage"), so an un-updated caller stays safe, just without persistence across tabs until it's updated.
  • 401 retry: a single automatic retry on 401 Unauthorized, even for non-idempotent calls — it rereads the token from ITokenProvider and retries once, to recover from a benign race between two concurrent calls on the same circuit racing the same refresh-token rotation (the request never reached application logic, so retrying is safe). A second consecutive 401 (a genuinely invalid token) is not retried again.
  • No exceptions from network/HTTP/JSON failures: they're translated into a normal KiApiResponse with Success = false.
  • Error message extraction: on a non-retryable HTTP error, ErrorMessage is populated from the error body even when your API doesn't respond in the KI envelope shape ({"errorMessage": "..."}). If the body is instead an ASP.NET Core ProblemDetails object ({"title","status","detail"} — the .NET default for unhandled model-validation/exception responses), detail is used, then title; if neither shape matches, the raw body is returned as-is. Only kicks in when the KI-shaped errorMessage would otherwise have been empty — an API that already populates it sees no change.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on KeyInteractive.Common.Api:

Package Downloads
KeyInteractive.Common.Api.Users

.NET client for the Key Interactive Users API: registration, login, enrollment, profile/password management, and the "Sign in with Key Interactive" authorize/consent/poll flow.

KeyInteractive.Common.Api.Tenants

.NET client for platforms integrating Key Interactive tenant (business) accounts: tenant login, tenant identity/profile (including PartnerCode), and team management (invite/list/remove/role members).

KeyInteractive.Messivo

.NET client for the Messivo WhatsApp Business Cloud API integration platform: send text/template/media messages, read conversations and message history, manage templates, phone numbers and contacts.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.3.0 133 9/3/2026
2.2.0 187 8/26/2026
2.1.0 230 8/10/2026
2.0.0 104 8/10/2026