> For the complete documentation index, see [llms.txt](https://docs.nerovasystems.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nerovasystems.com/sdks/available-sdks/dotnet.md).

# Use the .NET SDK

Call the Nerova API from C# with the Nerova.Sdk package: a typed client generated from the same committed OpenAPI contract the server publishes.

Call the Nerova API from C# with `Nerova.Sdk`: a typed client generated with [Microsoft Kiota](https://learn.microsoft.com/openapi/kiota/) from the same committed OpenAPI contract the server publishes. Every path, parameter, and model in the client exists because the contract says so.

## Prerequisites

{% hint style="info" %}

* .NET 10 SDK or later (the package targets `net10.0`).
* A Live API key (`nrv_live_`) from the [Quickstart](https://docs.nerovasystems.com/getting-started/quickstart).
* **The SDK is published to public NuGet as `Nerova.Sdk`** **`0.3.0-preview.1`**, a preview release: the API surface may change before 1.0.
  {% endhint %}

## 1. Install the SDK

Add the preview package from public NuGet:

```bash
dotnet add package Nerova.Sdk --version 0.3.0-preview.1
```

Pin the exact version. The package depends on `Microsoft.Kiota.Bundle`, which brings the abstractions, HTTP client, and serialization runtime, so the `using` directives below resolve without anything extra.

## 2. Construct the client

```csharp
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Http.HttpClientLibrary;
using Nerova.Sdk;

var value = Environment.GetEnvironmentVariable("NEROVA_API_KEY")
    ?? throw new InvalidOperationException("NEROVA_API_KEY is required.");

var authenticationProvider = new ApiKeyAuthenticationProvider(
    $"Bearer {value}",
    "Authorization",
    ApiKeyAuthenticationProvider.KeyLocation.Header
);
var client = new NerovaPartnerClient(new HttpClientRequestAdapter(authenticationProvider));
```

Two things to know:

* **Read the key from the environment.** Never hard-code it; the value is shown once at creation and cannot be retrieved again.
* **No base URL needed.** The client defaults to the single public host `https://api.nerovasystems.com`. Every key is a Live key (`nrv_live_`); there is no separate test host. See [Authentication](https://docs.nerovasystems.com/getting-started/authentication#api-keys).

The client mirrors the URL structure of the API: `client.Api.V1.Status`, `client.Api.V1.Context`, `client.Api.V1.Tenants`, and `client.Api.V1.Tenants[id]` for everything under one tenant (activation, capabilities, conversations, work, and the rest).

## 3. Make your first calls

```csharp
var status = await client.Api.V1.Status.GetAsync();
Console.WriteLine($"API {status?.ApiVersion} ({status?.Environment})");

var context = await client.Api.V1.Context.GetAsync();
Console.WriteLine($"environment: {context?.Environment}");
Console.WriteLine($"scopes: {string.Join(", ", context?.GrantedScopes ?? [])}");

var tenants = await client.Api.V1.Tenants.GetAsync();
foreach (var tenant in tenants ?? [])
{
    Console.WriteLine($"{tenant.Id}: {tenant.DisplayName} ({tenant.ProvisioningState})");
}
```

`GET /api/v1/tenants` returns a plain array of the tenants your key can see: `Id`, `ExternalReference`, `DisplayName`, `ProvisioningState`, `LifecycleState`, `PendingStep`, `LastErrorCode`, and `Version`.

## 4. Read a tenant's activation manifest

Take a tenant from the list above (or create one in step 5) and read its manifest:

```csharp
var tenantId = tenants!.First().Id!;
var manifest = await client.Api.V1
    .Tenants[tenantId]
    .Activation.Manifest.GetAsync();

Console.WriteLine("what still blocks activation:");
foreach (var reason in manifest?.BlockingReasons ?? [])
{
    Console.WriteLine($"{reason.Code}: {reason.Detail}");
}
```

The activation manifest is the heart of onboarding: it tells you exactly what still blocks a tenant from going live. The full walkthrough is [Provision and activate tenants](https://docs.nerovasystems.com/guides/partner-playbook).

## 5. Create a tenant

Mutations take an `Idempotency-Key` header, a caller-owned value that makes retries safe. Derive it from your own command identity as described in [Handle errors and retries](https://docs.nerovasystems.com/guides/error-handling#make-writes-safe-with-idempotency-keys):

```csharp
using Nerova.Sdk.Models;

var created = await client.Api.V1.Tenants.PostAsync(
    new CreateTenantV1Request
    {
        DisplayName = "Demo Salon",
        ExternalReference = "your-crm-id-123"
    },
    requestConfiguration => requestConfiguration.Headers.Add(
        "Idempotency-Key", "provision-tenant-your-crm-id-123")
);

Console.WriteLine($"tenant {created?.Id}: {created?.ProvisioningState}");
```

Replaying the same request with the same key returns the original result instead of creating a duplicate.

## 6. Handle errors

Failed calls throw the parsed [RFC 9457 problem document](https://docs.nerovasystems.com/api/conventions#errors) as `Nerova.Sdk.Models.ProblemDetails` (an `ApiException` subclass):

```csharp
using Nerova.Sdk.Models;

try
{
    var tenant = (await client.Api.V1.Tenants.GetAsync())!.First();
    await client.Api.V1.Tenants[tenant.Id].Conversations.GetAsync();
}
catch (ProblemDetails problem)
{
    problem.AdditionalData.TryGetValue("code", out var code);
    problem.AdditionalData.TryGetValue("correlationId", out var correlationId);

    // Branch on stable fields, never on message text.
    switch (problem.ResponseStatusCode)
    {
        case 429:
            // schedule a bounded retry
            break;
        case 403 when code as string == "partner.merchant_not_available":
            // this tenant is not granted to your key
            break;
        default:
            Console.Error.WriteLine(
                $"Nerova call failed: {code} (correlation {correlationId})");
            break;
    }
}
```

Quote the `correlationId` in [support reports](https://docs.nerovasystems.com/resources/support). The HTTP pipeline automatically retries `429`, `503`, and `504` responses a bounded number of times, honoring `Retry-After`; every other failure surfaces immediately, and broader retry policy stays in your hands, following [Handle errors and retries](https://docs.nerovasystems.com/guides/error-handling).

## 7. Read runtime state

Runtime surfaces (conversations, work ledger, notifications, incidents) read live merchant state for tenants your key is granted; other tenants return `partner.merchant_not_available`. Cursor-paginated endpoints all follow the same shape: pass `limit` and `cursor`, read `Items` and `NextCursor`:

```csharp
string? cursor = null;
do
{
    var page = await client.Api.V1.Tenants[tenantId].Work.GetAsync(
        requestConfiguration =>
        {
            requestConfiguration.QueryParameters.Limit = 50;
            requestConfiguration.QueryParameters.Cursor = cursor;
        });

    foreach (var item in page?.Items ?? []) Process(item);
    cursor = page?.NextCursor;
} while (cursor is not null);
```

Follow `NextCursor` until the server stops returning one.

## Keep the client current

The client is a deterministic output of the published contract. When the contract grows, a new package version ships on NuGet. Update by bumping the pinned version:

```bash
dotnet add package Nerova.Sdk --version <latest>
```

Newly published operations appear as new request builders. Clients must ignore unknown response fields: [additive change is allowed](https://docs.nerovasystems.com/api/conventions#versioning).

## Next steps

* The pipeline these calls implement: [Provision and activate tenants](https://docs.nerovasystems.com/guides/partner-playbook).
* Retry and conflict policy to wrap around the client: [Handle errors and retries](https://docs.nerovasystems.com/guides/error-handling).
* Prefer another language? [Use the TypeScript SDK](/sdks/available-sdks/typescript.md), [Use the Python SDK](/sdks/available-sdks/python.md), [Use the PHP SDK](/sdks/available-sdks/php.md), or [Use the Go SDK](/sdks/available-sdks/go.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nerovasystems.com/sdks/available-sdks/dotnet.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
