Official .NET client library for Vectra — the Intent-Aware Governance Gateway for Autonomous AI Agents.
Vectra sits between your AI agents and the outside world, enforcing governance policies, intercepting high-risk actions, and routing requests through Human-in-the-Loop (HITL) review when needed. This SDK gives your .NET applications strongly-typed access to every capability Vectra exposes: agent registration, JWT authentication, policy inspection, and HITL review workflows.
- Features
- Prerequisites
- Installation
- Quick Start
- Configuration
- API Reference
- Error Handling
- Dependency Injection
- Advanced Usage
- Running the Examples
- Running the Tests
- Contributing
- License
- Agent Management — Register, list, assign policies to, and delete AI agents.
- JWT Authentication — Exchange agent credentials for a scoped Bearer token with one call.
- Policy Inspection — List and inspect the governance policies configured in your gateway.
- HITL Review Workflows — Retrieve pending review requests, then approve or deny them programmatically.
- First-class DI support — Single
AddVectraClient(...)extension wires up all typed HTTP clients. - Automatic Bearer token injection — Set
BearerTokenonce; every outgoing request carries the header. - Structured exceptions —
VectraApiExceptionandVectraAuthenticationExceptionexpose the HTTP status code and the server's error payload. - Full cancellation support — Every async method accepts a
CancellationToken. - Pagination — All list operations accept
pageandpageSizeparameters.
| Requirement | Version |
|---|---|
| .NET | 10.0+ |
| Vectra Gateway | Running and reachable (default: http://localhost:7080) |
dotnet add package Vectra.Client.NetOr via the NuGet Package Manager:
Install-Package Vectra.Client.Net
// Program.cs — ASP.NET Core / Generic Host
builder.Services.AddVectraClient(options =>
{
options.BaseUrl = "http://localhost:7080";
options.BearerToken = "your-jwt-token"; // optional; see Token Authentication
});// Any service that needs Vectra
public class MyService(IVectraClient vectra)
{
public async Task RunAsync(CancellationToken ct)
{
// List registered agents
var agents = await vectra.Agents.ListAsync(page: 1, pageSize: 10, ct);
foreach (var agent in agents)
Console.WriteLine($"[{agent.Status,-8}] {agent.Name} id={agent.AgentId}");
// List governance policies
var policies = await vectra.Policies.ListAsync(cancellationToken: ct);
foreach (var policy in policies)
Console.WriteLine($"{policy.PolicyName} owner={policy.Owner}");
// Check the HITL queue
var pending = await vectra.Hitl.GetAllPendingAsync(cancellationToken: ct);
Console.WriteLine($"Pending HITL reviews: {pending.Count}");
}
}Register the SDK with AddVectraClient and supply a configuration delegate:
builder.Services.AddVectraClient(options =>
{
options.BaseUrl = "http://localhost:7080"; // required
options.BearerToken = "<jwt>"; // optional — injected on every request
options.Timeout = TimeSpan.FromSeconds(30);// default: 30 s
options.ThrowOnError = true; // default: true
});You can also pass a pre-built VectraClientOptions instance (useful when binding from appsettings.json):
var opts = builder.Configuration
.GetSection("Vectra")
.Get<VectraClientOptions>()!;
builder.Services.AddVectraClient(opts);| Property | Type | Default | Description |
|---|---|---|---|
BaseUrl |
string |
(required) | Base URL of the Vectra gateway. |
BearerToken |
string? |
null |
Static JWT injected as Authorization: Bearer <token>. |
Timeout |
TimeSpan |
00:00:30 |
Per-request HTTP timeout. |
ThrowOnError |
bool |
true |
Throw VectraApiException on non-2xx responses. |
All operations are accessed through the IVectraClient facade:
IVectraClient
├── .Agents → IVectraAgentClient
├── .Policies → IVectraPolicyClient
├── .Hitl → IVectraHitlClient
└── .Tokens → IVectraTokenClient
// List agents (paginated)
IReadOnlyList<AgentSummary> agents = await vectra.Agents.ListAsync(page: 1, pageSize: 25, ct);
// Register a new agent
RegisterAgentResult result = await vectra.Agents.RegisterAsync(new RegisterAgentRequest
{
Name = "my-research-agent",
OwnerId = "team-backend",
ClientSecret = "super-secret-value" // store securely — Vectra hashes it
}, ct);
Console.WriteLine($"New agent ID: {result.AgentId}");
// Assign a policy to an agent
await vectra.Agents.AssignPolicyAsync(result.AgentId, new AssignPolicyRequest
{
PolicyName = "strict-outbound"
}, ct);
// Delete an agent
await vectra.Agents.DeleteAsync(result.AgentId, ct);| Property | Type | Description |
|---|---|---|
AgentId |
Guid |
Unique identifier. |
Name |
string |
Display name. |
Status |
AgentStatus |
Current lifecycle status (e.g. Active, Inactive). |
PolicyName |
string? |
Name of the assigned policy, or null. |
Exchange an agent's credentials for a scoped JWT:
GenerateTokenResult token = await vectra.Tokens.GenerateAsync(new GenerateTokenRequest
{
AgentId = agentId,
ClientSecret = "super-secret-value"
}, ct);
// Apply the token to all subsequent SDK calls
optionsMonitor.CurrentValue.BearerToken = token.AccessToken;public sealed class TokenRefresher(
IVectraClient vectra,
IOptionsMonitor<VectraClientOptions> options,
Guid agentId,
string clientSecret)
{
private readonly SemaphoreSlim _lock = new(1, 1);
private DateTime _expiresAt;
public async Task EnsureValidTokenAsync(CancellationToken ct = default)
{
if (DateTime.UtcNow < _expiresAt) return;
await _lock.WaitAsync(ct);
try
{
if (DateTime.UtcNow >= _expiresAt)
{
var result = await vectra.Tokens.GenerateAsync(
new GenerateTokenRequest { AgentId = agentId, ClientSecret = clientSecret }, ct);
options.CurrentValue.BearerToken = result.AccessToken;
_expiresAt = DateTime.UtcNow.AddMinutes(55); // refresh before expiry
}
}
finally { _lock.Release(); }
}
}// List all policies (paginated)
IReadOnlyList<PolicySummary> policies = await vectra.Policies.ListAsync(page: 1, pageSize: 25, ct);
// Get full policy details including rules
PolicyDetails details = await vectra.Policies.GetAsync("strict-outbound", ct);
Console.WriteLine($"Policy : {details.Name}");
Console.WriteLine($"Default: {details.Default}");
foreach (var rule in details.Rules)
Console.WriteLine($" [{rule.Effect}] {rule.Description}");| Property | Type | Description |
|---|---|---|
Name |
string |
Policy identifier. |
Description |
string? |
Human-readable description. |
Owner |
string |
Team or user that owns this policy. |
Default |
PolicyType |
Effect applied when no rule matches. |
Rules |
IReadOnlyList<PolicyRule> |
Ordered list of evaluation rules. |
CreatedOn |
DateTime? |
Creation timestamp. |
When Vectra intercepts a high-risk agent request it raises a HITL item. Your operators (or automation) can then approve or deny the item, controlling whether the original request is replayed upstream.
// 1. Get the queue of pending reviews
IReadOnlyList<PendingHitlRequest> queue =
await vectra.Hitl.GetAllPendingAsync(page: 1, pageSize: 25, ct);
// 2. Inspect a specific item
HitlStatusResponse status = await vectra.Hitl.GetStatusAsync(queue[0].Id, ct);
Console.WriteLine($"Status : {status.Status}");
Console.WriteLine($"Method : {status.Method} URL: {status.Url}");
Console.WriteLine($"Reason : {status.Reason}");
// 3. Approve — gateway replays the request to the upstream service
await vectra.Hitl.ApproveAsync(queue[0].Id, new ReviewDecisionRequest
{
Comment = "Verified safe by on-call engineer."
}, ct);
// 4. Deny — gateway permanently blocks the request
await vectra.Hitl.DenyAsync(queue[0].Id, new ReviewDecisionRequest
{
Comment = "Blocked: unexpected data-exfiltration attempt."
}, ct);| Property | Type | Description |
|---|---|---|
Id |
string |
Unique identifier of the HITL item. |
Method |
string |
HTTP method of the intercepted request. |
Url |
string |
Target URL of the intercepted request. |
Reason |
string |
Gateway's classification reason for interception. |
CreatedAt |
DateTime |
When the item entered the queue. |
The SDK uses a structured exception hierarchy:
VectraException (base — all SDK errors)
├── VectraApiException (non-2xx HTTP response)
└── VectraAuthenticationException (401 / 403 responses)
try
{
var policy = await vectra.Policies.GetAsync("unknown-policy", ct);
}
catch (VectraAuthenticationException ex)
{
// 401 or 403 — re-authenticate or surface to the user
Console.WriteLine($"Auth error [{ex.StatusCode}]: {ex.Message}");
}
catch (VectraApiException ex) when (ex.StatusCode == 404)
{
Console.WriteLine("Policy not found.");
}
catch (VectraApiException ex)
{
// All other non-2xx responses
Console.WriteLine($"API error [{ex.StatusCode}]: {ex.Message}");
// Structured payload from the server (when available)
if (ex.ApiError is { } err)
Console.WriteLine($"Server detail: {err.Detail}");
}
catch (VectraException ex)
{
// SDK-level error (e.g. serialization, configuration)
Console.WriteLine($"SDK error: {ex.Message}");
}
catch (OperationCanceledException)
{
Console.WriteLine("Request timed out or was cancelled.");
}Set options.ThrowOnError = false to suppress automatic exception throwing and handle HTTP errors manually via status codes.
The SDK integrates with Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Http.
// Program.cs
builder.Services.AddVectraClient(options =>
{
options.BaseUrl = builder.Configuration["Vectra:BaseUrl"]!;
options.BearerToken = builder.Configuration["Vectra:BearerToken"];
});// Inject into any service
public class AgentOrchestrator(IVectraClient vectra) { ... }var services = new ServiceCollection();
services.AddVectraClient(opts =>
{
opts.BaseUrl = "http://localhost:7080";
opts.BearerToken = "<jwt>";
});
await using var provider = services.BuildServiceProvider();
var vectra = provider.GetRequiredService<IVectraClient>();| Interface | Implementation | Lifetime |
|---|---|---|
IVectraClient |
VectraClient |
Transient |
IVectraAgentClient |
AgentClient |
Typed HttpClient |
IVectraPolicyClient |
PolicyClient |
Typed HttpClient |
IVectraHitlClient |
HitlClient |
Typed HttpClient |
IVectraTokenClient |
TokenClient |
Typed HttpClient |
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var agents = await vectra.Agents.ListAsync(cancellationToken: cts.Token);optionsMonitor.OnChange(opts =>
{
logger.LogInformation("Vectra BearerToken rotated at {Time}", DateTimeOffset.UtcNow);
});Poll the HITL queue on a timer and auto-approve safe requests:
public class HitlMonitorService(IVectraClient vectra, ILogger<HitlMonitorService> logger)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var pending = await vectra.Hitl.GetAllPendingAsync(cancellationToken: stoppingToken);
foreach (var request in pending)
{
// Apply your own approval logic here
logger.LogInformation("Pending HITL: {Id} — {Method} {Url}", request.Id, request.Method, request.Url);
}
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
}The examples/ project contains ten annotated runnable examples covering every SDK feature:
| # | Name | What it demonstrates |
|---|---|---|
| 01 | Quick Start | List agents, policies, and HITL queue in a few lines |
| 02 | Token Authentication | Credential exchange, token refresh, auth error handling |
| 03 | Agent Management | Register, list, assign policy, delete agents |
| 04 | Policy Inspection | List policies, drill into rules |
| 05 | HITL Workflow | Pending queue, status inspection, approve/deny |
| 06 | Error Handling | All exception types, when-clause patterns |
| 07 | Background HITL Monitor | Polling queue as a hosted background service |
| 08 | Agent Lifecycle | Full create → configure → decommission flow |
| 09 | Advanced Configuration | Runtime options, manual DI, per-request timeouts |
| 10 | Pagination & Bulk | Iterating large result sets efficiently |
# Make sure the Vectra gateway is running first
cd examples
dotnet runEdit examples/Program.cs to configure the gateway URL, Bearer token, and which example to execute.
dotnet test tests/Vectra.Client.UnitTestsThe test suite uses xUnit and covers all HTTP clients, the DI extensions, exception types, model validation, and internal helpers via mock HTTP message handlers — no live gateway required.
- Fork the repository and create a feature branch.
- Add or update tests for any changed behaviour.
- Ensure
dotnet buildanddotnet testboth pass. - Open a pull request against
main.
Please follow the existing code style (nullable reference types enabled, required properties, XML doc comments on public API).
© Cortexium Labs. Licensed under the Apache License, Version 2.0.