Skip to Content
This documentation is provided with the HEAT environment and is relevant for this HEAT instance only.
APIHEAT.Common Client (C#)

HEAT.Common Client (C#)

HEAT.Common is a .NET library that provides a single, developer-friendly client for uploading data to HEAT and managing sessions. You configure where HEAT is and how to sign in (token or username/password); the client handles authentication, health checks, and calls to the HEAT V2 API so you can focus on creating sessions and uploading data.

This page is the main documentation for using the HEAT.Common runtime. For a runnable end-to-end example, see Example project and the runtimes/csharp/HEAT.Common.Example project in the repo.


Overview

What HEAT.Common does

  • Connects to a HEAT instance (external URL or in-cluster) and validates connectivity via the V2 health endpoint.
  • Authenticates using a long-lived token (e.g. offline token) or username/password; with password auth, the client obtains and caches access/refresh tokens and retries on 401.
  • Creates and manages sessions — create a session in a project (with optional session template), create or join shared sessions, close shared sessions, and fetch session details.
  • Uploads data — send raw bytes (files or streams) to a node instance in a session; HEAT stores the data and creates a node output.
  • Enumerates metadata — list session templates, projects, platform configuration by prefix, and node outputs so you can discover templates, pick projects, read config from the environment, and verify uploads.

You do not need to call HEAT Auth or V2 endpoints directly; the client wraps those behind a simple IHeatClient interface.

When to use HEAT.Common

Use HEAT.Common when you are building a .NET application (e.g. console app, web API, worker, or simulator integration) that needs to:

  • Push data into HEAT (create a session and upload data to node instances).
  • Create or join shared sessions and close them when done.
  • Discover session templates, projects, or platform configuration before creating sessions.

If you are building a runner (containerized processor that runs inside HEAT and processes node tasks), use the HEAT runtime for your language (e.g. Python heat-runtime) instead; runners are invoked by HEAT and do not use HEAT.Common to create sessions.


Prerequisites

  • .NET 8.0 (or the target framework your app uses; HEAT.Common targets net8.0).
  • A HEAT instance with the V2 API and HEAT Auth reachable (either from your machine for external use, or from inside the cluster for in-cluster use).
  • Authentication: either a long-lived token (e.g. offline token) or username and password for HEAT Auth.
  • At least one project and one session template in HEAT if you want to create sessions and upload data (you can create these in Cluster Manager or via the API).

Installation

HEAT.Common is distributed as a project reference from the HEAT repo (or as a built assembly). There is no public NuGet package at this time.

Add the project reference

  1. Clone or copy the HEAT repo (or the runtimes/csharp folder).
  2. In your .NET project, add a reference to HEAT.Common:
<ItemGroup> <ProjectReference Include="path/to/HEAT/runtimes/csharp/HEAT.Common/HEAT.Common.csproj" /> </ItemGroup>
  1. Add the packages required for dependency injection and HTTP (if not already present):
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />

HEAT.Common itself references Microsoft.Extensions.Http, Microsoft.Extensions.Options, and Microsoft.Extensions.Configuration.Binder; your app typically needs the DI and configuration packages (e.g. Microsoft.Extensions.Hosting or Microsoft.Extensions.DependencyInjection) to call AddHeatClient.


Quick start

  1. Register the client with your service collection (e.g. in Program.cs or Startup.cs):
using HEAT.Common; // Option A: from configuration (e.g. appsettings.json "Heat:Client" section) services.AddHeatClient(Configuration); // Option B: configure in code services.AddHeatClient(opts => { opts.BaseUrl = "https://your-env.heatvr.io"; opts.Token = "your-offline-or-access-token"; });
  1. Resolve and use the client:
var client = serviceProvider.GetRequiredService<IHeatClient>(); await client.ConnectAsync(); // optional: validate connectivity (or rely on AutoConnect) var session = await client.CreateSessionAsync(projectId, new CreateSessionRequest { Name = "My Session", Email = "user@example.com", SimulationName = "MySim" }); if (session?.NodeInstances?.Count > 0) { await using var data = new MemoryStream(Encoding.UTF8.GetBytes("your payload")); var output = await client.UploadNodeOutputAsync(session.NodeInstances[0].Id, data); }

Authentication

HEAT.Common supports two authentication modes. You must set one; the client will throw HeatClientConfigurationException if neither is configured.

Token authentication

Use a long-lived token (e.g. an offline token) when your app runs without user interaction or when you want to avoid storing username/password.

  • Set HeatClientOptions.Token (and BaseUrl for external HEAT).
  • The client sends the token on every V2 request; no refresh is performed.
  • For in-cluster use (e.g. a pod inside the same Kubernetes cluster as HEAT), use HeatClientOptions.ForInClusterToken("your-token") so the client targets http://heat-v2-api:5000/api/v2 and http://heat-auth without a base URL.

Example (external):

services.AddHeatClient(opts => { opts.BaseUrl = "https://your-env.heatvr.io"; opts.Token = "your-offline-token"; });

Example (in-cluster):

services.AddHeatClient(opts => { opts.InCluster = true; opts.Token = "your-token"; });

Username and password

Use username and password when you have interactive or service credentials. The client will:

  • Call HEAT Auth to obtain an access token (and optionally a refresh token).
  • Cache the access token and send it on every V2 request.
  • On 401 Unauthorized, attempt to refresh the token once and retry the request (for non-streaming calls).

Set HeatClientOptions.Username and HeatClientOptions.Password; optionally set RememberMe = true (default) to request a refresh token.

services.AddHeatClient(opts => { opts.BaseUrl = "https://your-env.heatvr.io"; opts.Username = "service-account"; opts.Password = "secret"; });

In-cluster vs external

ScenarioBaseUrlInClusterAuth
App outside HEAT (e.g. your server)HEAT ingress URL (e.g. https://your-env.heatvr.io)falseToken or Username/Password
App inside HEAT cluster (e.g. another service)Not requiredtrueToken (or Username/Password; auth service is http://heat-auth)

Registration (dependency injection)

Register the client with one of the following.

From configuration

Bind options from the Heat:Client section (e.g. in appsettings.json):

{ "Heat": { "Client": { "BaseUrl": "https://your-env.heatvr.io", "Token": "your-token" } } }
services.AddHeatClient(Configuration);

From code

Configure options in code (e.g. from environment variables):

services.AddHeatClient(opts => { opts.BaseUrl = Environment.GetEnvironmentVariable("HEAT_BASE_URL"); opts.Token = Environment.GetEnvironmentVariable("HEAT_TOKEN"); // or: opts.Username = ...; opts.Password = ...; });

The client is registered as a singleton (IHeatClient / HeatClient) and uses a named HttpClient ("HeatClient"). Do not register multiple IHeatClient unless you use different option instances and named clients.


API reference

All methods are on IHeatClient and are async. Cancellation is supported via CancellationToken; omit it or pass default if not needed.

Connect and health

ConnectAsync(CancellationToken ct = default)

Validates that the HEAT environment is reachable. For external HEAT, calls GET /api/v2/health; 200 OK means the environment is OK. Call this explicitly if HeatClientOptions.AutoConnect is false; otherwise the client will connect on first use of any session/upload method.

Throws: HeatConnectionException if the environment is not reachable or did not return OK.

await client.ConnectAsync();

Discovery (templates, config, projects)

GetSessionTemplatesAsync(CancellationToken ct = default)

Returns all session templates. Use a template’s Id in CreateSessionRequest.SessionTemplateId when creating a session.

Returns: IReadOnlyList<SessionTemplateInfo> (Id, Name, Description, Configuration).

var templates = await client.GetSessionTemplatesAsync(); foreach (var t in templates) Console.WriteLine($"Template {t.Id}: {t.Name}");

GetPlatformConfigurationByPrefixAsync(string prefix, CancellationToken ct = default)

Returns platform configuration keys whose name equals or starts with the given prefix (e.g. "public_api_v1"). Useful for reading config values from the environment without hardcoding. External users are restricted to prefixes allowed by HEAT (e.g. public_api_v1).

Returns: IReadOnlyDictionary<string, object>. Empty if no keys match or the API returns 404.

var config = await client.GetPlatformConfigurationByPrefixAsync("public_api_v1"); if (config.TryGetValue("SomeKey", out var value)) Console.WriteLine(value);

GetProjectsAsync(CancellationToken ct = default)

Returns all projects. Use a project’s Id as projectId when calling CreateSessionAsync or CreateOrJoinSharedSessionAsync.

Returns: IReadOnlyList<ProjectInfo> (Id, HexId, Title, Description, DefaultSessionTemplateId).

var projects = await client.GetProjectsAsync(); int projectId = projects[0].Id;

Sessions

CreateSessionAsync(int projectId, CreateSessionRequest request, CancellationToken ct = default)

Creates a new session for the given project. You can set request.SessionTemplateId to use a specific template, or leave it null to use the project’s default template.

Returns: SessionInfo (Id, Name, Email, ProjectId, SessionTemplateId, SimulationName, NodeInstances). Use NodeInstances[].Id to upload data. Returns null only if the API returns no data.

Throws: HeatApiException on 404 (e.g. project not found), 400, etc.

var session = await client.CreateSessionAsync(projectId, new CreateSessionRequest { Name = "Training run 001", Email = "user@example.com", SimulationName = "MySimulator", SessionTemplateId = 2 // optional }); Guid sessionId = session.Id; var firstNodeId = session.NodeInstances?[0].Id;

CreateOrJoinSharedSessionAsync(int projectId, CreateSharedSessionRequest request, CancellationToken ct = default)

Creates a new shared session or joins an existing one within a time window (based on simulation name, machine group, etc.). Use this for scenarios where multiple clients contribute to the same logical session.

Returns: SessionInfo (new or existing session with node instances).

var session = await client.CreateOrJoinSharedSessionAsync(projectId, new CreateSharedSessionRequest { Name = "Shared session", SimulationName = "Sim", Email = "user@example.com", SessionTemplateId = 1, StartTimeUtc = DateTime.UtcNow, MachineGroup = 1, TimeWindowSeconds = 300 });

GetSessionAsync(Guid sessionId, CancellationToken ct = default)

Fetches a session by ID. Returns the session with node instances, or null if not found.

var session = await client.GetSessionAsync(sessionId);

CloseSessionAsync(Guid sessionId, CloseSessionRequest request, CancellationToken ct = default)

Closes a shared session. Idempotent; call when the shared session is finished.

Returns: true if the close succeeded.

await client.CloseSessionAsync(sessionId, new CloseSessionRequest { Email = "user@example.com", Reason = "Completed", CloseAt = DateTime.UtcNow });

Upload and outputs

UploadNodeOutputAsync(int nodeInstanceId, Stream data, CancellationToken ct = default)

Uploads raw data to a node instance. The node instance must be configured with a data source (e.g. S3-compatible storage); HEAT stores the stream and creates a node output. Use a node instance ID from SessionInfo.NodeInstances (e.g. after creating or fetching a session).

Returns: NodeOutputInfo (Id, NodeInstanceId, CreatedAt, DataPath, etc.) or null if the API returned no data.

Throws: HeatApiException on 404 (node not found), 400 (e.g. invalid node configuration), etc.

await using var stream = File.OpenRead("data.json"); var output = await client.UploadNodeOutputAsync(nodeInstanceId, stream); Console.WriteLine($"Created output {output?.Id} at {output?.CreatedAt}");

GetNodeOutputsAsync(int nodeInstanceId, CancellationToken ct = default)

Returns all outputs (uploaded data) for the given node instance, ordered by creation time. Use this to verify that data was uploaded. Returns an empty list if the node has no outputs or the API returns 404.

Returns: IReadOnlyList<NodeOutputInfo>.

var outputs = await client.GetNodeOutputsAsync(nodeInstanceId); foreach (var o in outputs) Console.WriteLine($"Output {o.Id} at {o.CreatedAt}");

Models (DTOs)

TypePurpose
CreateSessionRequestName, Email, SimulationName (required); SessionTemplateId, Metadata (optional).
CreateSharedSessionRequestName, SimulationName, Email, SessionTemplateId, StartTimeUtc, MachineGroup (required); TimeWindowSeconds (optional, default 300).
CloseSessionRequestEmail, Reason, CloseAt (required).
SessionInfoId, Name, Email, ProjectId, SessionTemplateId, SimulationName, NodeInstances (list of NodeInstanceInfo).
NodeInstanceInfoId, Name. Use Id to upload data or list outputs.
NodeOutputInfoId, NodeInstanceId, Configuration, CreatedAt, DataSourceId, DataPath, OutputIdentifier.
SessionTemplateInfoId, Name, Description, Configuration. Use Id in CreateSessionRequest.SessionTemplateId.
ProjectInfoId, HexId, Title, Description, DefaultSessionTemplateId. Use Id as projectId when creating sessions.

All request types live in the HEAT.Common.Models namespace and match the HEAT V2 API shape (camelCase in JSON).


Configuration reference (Heat:Client)

When using AddHeatClient(Configuration), options are bound from the Heat:Client section:

KeyDescription
BaseUrlBase URL for external HEAT (e.g. https://your-env.heatvr.io). Required when not in-cluster. Do not include a trailing slash.
InClusterWhen true, use in-cluster defaults (V2 API at http://heat-v2-api:5000/api/v2, Auth at http://heat-auth).
TokenLong-lived token (e.g. offline token). Use this or Username+Password.
UsernameUsername for password auth.
PasswordPassword for password auth.
RememberMeWhen using username/password, request a refresh token (default true).
AutoConnectWhen true (default), the client runs the health check on first use if you have not called ConnectAsync() already. Set to false to require an explicit ConnectAsync() before any other operations.

Example project

A full working example that demonstrates the entire upload workflow is in the repo at runtimes/csharp/HEAT.Common.Example. It:

  1. Instantiates a HEAT client from environment variables.
  2. Connects and enumerates session templates and (optionally) platform config.
  3. Lists projects and picks one (or uses HEAT_PROJECT_ID).
  4. Creates a session with the desired (or default) template.
  5. Uploads a small text payload to the first node instance.
  6. Lists outputs on that node instance to verify the upload.

How to run the example

Prerequisites: A running HEAT instance, and either a token or username/password.

Set environment variables, then run:

export HEAT_BASE_URL=https://your-env.heatvr.io export HEAT_TOKEN=your-offline-token # Or: export HEAT_USERNAME=...; export HEAT_PASSWORD=... dotnet run --project runtimes/csharp/HEAT.Common.Example

Optional:

  • HEAT_PROJECT_ID — use this project ID instead of the first in the list.
  • HEAT_SESSION_TEMPLATE_ID — use this template when creating the session; otherwise the project’s default is used.
  • HEAT_CONFIG_PREFIX — if set, the example fetches and prints platform config for this prefix (e.g. public_api_v1).

See runtimes/csharp/HEAT.Common.Example/README.md in the repo for full details.


End-to-end workflow (upload data)

A typical flow to upload data to HEAT:

  1. Connectawait client.ConnectAsync(); (or rely on AutoConnect).
  2. Discover — Optionally call GetSessionTemplatesAsync(), GetPlatformConfigurationByPrefixAsync(prefix), and GetProjectsAsync() to choose a project and template.
  3. Create sessionCreateSessionAsync(projectId, new CreateSessionRequest { ... }). Use SessionTemplateId from a template or leave null for the project default.
  4. Get node instance — From session.NodeInstances, pick the node (e.g. the first input node) and use its Id.
  5. UploadUploadNodeOutputAsync(nodeInstanceId, stream).
  6. VerifyGetNodeOutputsAsync(nodeInstanceId) to confirm the new output appears.
  7. Close (if shared session) — CloseSessionAsync(sessionId, new CloseSessionRequest { ... }).

Error handling

The client throws explainable exceptions; the message describes what went wrong and what to try next.

ExceptionWhen it is thrownWhat to do
HeatClientConfigurationExceptionInvalid options (e.g. neither Token nor Username+Password set; BaseUrl missing when not InCluster).Set either Token or both Username and Password. Ensure BaseUrl is set when InCluster is false.
HeatConnectionExceptionHealth check or connectivity failed (GET /api/v2/health non-200 or network error).Check HEAT base URL, network/firewall, and that the V2 API is running. For external use, use the ingress URL.
HeatAuthenticationExceptionAuth failed (invalid or expired token, wrong username/password, 401 from auth or V2).For token: obtain a new or offline token. For username/password: check credentials.
HeatApiExceptionV2 API returned 4xx/5xx for a session or upload operation.Inspect Operation, StatusCode, and optional ResponseBody. Fix the request (e.g. valid projectId, node instance from the session).

Example:

try { await client.ConnectAsync(); var session = await client.CreateSessionAsync(projectId, request); // ... } catch (HeatConnectionException ex) { _logger.LogError(ex, "HEAT is not reachable"); } catch (HeatAuthenticationException ex) { _logger.LogError(ex, "Authentication failed"); } catch (HeatApiException ex) { _logger.LogError(ex, "API error: {Operation} {StatusCode}", ex.Operation, ex.StatusCode); }

Troubleshooting

SymptomLikely causeAction
”HEAT environment at … did not return OK”Wrong base URL or V2 API down.Verify base URL (no trailing slash). Ensure GET /api/v2/health returns 200 from your environment.
”Authentication failed”Invalid/expired token or wrong username/password.For token: get a new or offline token. For password: check credentials.
”CreateSession failed (404): Project not found”The projectId does not exist.Use an ID from GetProjectsAsync() or Cluster Manager.
”UploadNodeOutput failed (404)“Node instance ID invalid or not in the session.Use an ID from session.NodeInstances after creating or fetching the session.
”UploadNodeOutput failed (400)“Node configuration invalid (e.g. missing DataSourceName).Fix the session template / node template configuration in HEAT so the node has a valid data source.
Connection timeoutsApp outside cluster cannot reach HEAT.Use the public ingress URL; check firewall/DNS.
GetNodeOutputsAsync returns empty listNode has no outputs yet, or node not found (client treats 404 as empty).Confirm you uploaded to this node instance; confirm node instance ID is correct.

Source code and example: runtimes/csharp/HEAT.Common and runtimes/csharp/HEAT.Common.Example in the HEAT repository.