# .NET SDK

Use the `Cronitor.Sdk` NuGet package to report job runs, heartbeats, and metrics to Cronitor from C# and other .NET languages.

## Install

```bash
dotnet add package Cronitor.Sdk
```

The package id is `Cronitor.Sdk`. The namespace is `Cronitor`. The package targets `netstandard2.0` and `net8.0`.

## Configure

Set the `CRONITOR_API_KEY` environment variable to your SDK Integration key. Copy the key from Settings → API Keys at https://cronitor.io/app/settings/api. Keep the key in the environment or a secret store of the runtime. Do not commit it.

`new CronitorOptions()` reads `CRONITOR_API_KEY`. Do not set `ApiKey` in code:

```csharp
using Cronitor;

var cronitor = new CronitorClient(new CronitorOptions());
```

## Monitor a job

Wrap the main function of the job with `JobAsync`:

```csharp
using Cronitor;

var cronitor = new CronitorClient(new CronitorOptions());

await cronitor.JobAsync("send-invoices", async () =>
{
    await SendInvoicesAsync();
});
```

- Before the function starts, the SDK sends a `run` event.
- When the function returns, the SDK sends a `complete` event with the elapsed time as the `duration` metric.
- When the function throws, the SDK sends a `fail` event with the exception message and the `duration` metric. Then it throws the exception again.
- The events go to the monitor with the key `send-invoices`. If no monitor has this key, Cronitor creates one on the first event.

For synchronous code, use `cronitor.Job("send-invoices", () => SendInvoices())`. The SDK sends the return value of the function as the message of the `complete` event. To not send it, give `new JobOptions { LogOutput = false }` as the third argument.

To send the events yourself, use a monitor:

```csharp
var monitor = cronitor.Monitor("send-invoices");
await monitor.PingAsync(new PingOptions { State = PingState.Run });
await monitor.PingAsync(new PingOptions { State = PingState.Complete }); // or PingState.Fail
```

## Send a heartbeat

Send a ping each time the process completes a unit of useful work:

```csharp
var monitor = cronitor.Monitor("queue-worker");
await monitor.PingAsync(new PingOptions { Message = "Alive!" });
```

To attach metrics, set `Metrics`. The SDK sends each item as `metric=name:value`:

```csharp
await monitor.PingAsync(new PingOptions
{
    Metrics = new Dictionary<string, double> { ["count"] = 100, ["error_count"] = 3 },
});
```

If you only want to record custom metrics, use a heartbeat monitor. See [Custom Metrics](https://cronitor.io/docs/custom-metrics.md).

## Verify

After one real run, check the state of the monitor. A successful telemetry response shows only that Cronitor received the request. It does not show that Cronitor stored the event or matched it to a monitor.

With the [MCP server](https://cronitor.io/docs/mcp-server.md):

```text
get_status({"key": "send-invoices"})
```

With [CronitorCLI](https://cronitor.io/docs/using-cronitor-cli.md):

```bash
cronitor status send-invoices
```

`PingAsync` does not throw. It returns `false` and logs an error when no API key is set or when the ping fails after retries. The SDK logs through an `ILogger` only when you give one to the client.

## Reference

- [cronitor-dotnet on GitHub](https://github.com/cronitorio/cronitor-dotnet): the full README, including monitor attributes and YAML configuration.
- [Telemetry API](https://cronitor.io/docs/telemetry-api.md): the events and parameters that the SDK sends.
