Cronitor.Sdk 1.0.1

dotnet add package Cronitor.Sdk --version 1.0.1
                    
NuGet\Install-Package Cronitor.Sdk -Version 1.0.1
                    
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="Cronitor.Sdk" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cronitor.Sdk" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Cronitor.Sdk" />
                    
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 Cronitor.Sdk --version 1.0.1
                    
#r "nuget: Cronitor.Sdk, 1.0.1"
                    
#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 Cronitor.Sdk@1.0.1
                    
#: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=Cronitor.Sdk&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Cronitor.Sdk&version=1.0.1
                    
Install as a Cake Tool

Cronitor .NET Library

Test

Cronitor provides end-to-end monitoring for background jobs, websites, APIs, and anything else that can send or receive an HTTP request. This library provides convenient access to the Cronitor API from applications written in C# and other .NET languages. See our API docs for detailed references on configuring monitors and sending telemetry pings.

In this guide:

Installation

dotnet add package Cronitor.Sdk

The package id is Cronitor.Sdk; the namespace is Cronitor.

The package targets netstandard2.0 and net8.0.

Monitoring Background Jobs

The job wrapper is a lightweight way to monitor any background task regardless of how it is executed. It will send a telemetry event before calling your function and after it exits. If your function throws, a fail event will be sent (and the exception rethrown).

using Cronitor;

// your api keys can found here - https://cronitor.io/settings/api
var cronitor = new CronitorClient(new CronitorOptions { ApiKey = "apiKey123" });

// Wrap any function to monitor it.
// If no monitor matches the provided key, one will be created automatically.
var invoiceCount = await cronitor.JobAsync("send-invoices", async () =>
{
    return await SendInvoicesAsync();
});

// Synchronous code works too.
cronitor.Job("send-invoices", () => SendInvoices());

The job wrapper sends run when your function starts and complete (with the stringified return value as the message and the elapsed duration metric) when it returns. Pass new JobOptions { LogOutput = false } to omit the return value, or new JobOptions { Environment = "staging" } to tag the events with an environment.

You can provide monitor attributes that will be synced when your app starts

To sync attributes, provide an API key with monitor:write privileges. Attributes are queued as jobs run and are sent to Cronitor when you call SyncMonitorsAsync (for example at startup or on a schedule); no background thread is started.

using Cronitor;

// Copy your SDK Integration key from https://cronitor.io/app/settings/api
var cronitor = new CronitorClient(new CronitorOptions { ApiKey = "apiKey123" });

await cronitor.JobAsync("send-invoices", SendInvoicesAsync, new JobOptions
{
    Attributes = new Dictionary<string, object?>
    {
        ["schedule"] = "0 8 * * *",
        ["notify"] = new[] { "devops-alerts" },
    },
});

await cronitor.SyncMonitorsAsync(); // PUTs the queued attributes to the Monitor API

Sending Telemetry Events

If you want to send heartbeat events, or want finer control over when/how telemetry events are sent for your jobs, you can create a monitor instance and call PingAsync.

using Cronitor;

// your api keys can found here - https://cronitor.io/settings/api
var cronitor = new CronitorClient(new CronitorOptions
{
    ApiKey = "apiKey123",
    Environment = "staging", // optional
});

var monitor = cronitor.Monitor("heartbeat-monitor");
await monitor.PingAsync(); // send a heartbeat event

// optional params can be passed through PingOptions.
// for a complete list see https://cronitor.io/docs/telemetry-api#parameters
await monitor.PingAsync(new PingOptions
{
    State = PingState.Run, // Run|Complete|Fail used to measure lifecycle of a job, Ok used for manual reset only.
    Message = "", // message that will be displayed in alerts as well as monitor activity panel on your dashboard.
    Metrics = new Dictionary<string, double>
    {
        ["duration"] = 100, // how long the job ran (complete|fail only). cronitor will calculate this when not provided
        ["count"] = 4500, // if your job is processing a number of items you can report a count
        ["error_count"] = 10, // the number of errors that occurred while this job was running
    },
});

PingAsync never throws: it returns true when the ping was accepted and false (logging an error) when no API key is configured or the ping could not be delivered after retries.

Configuring Monitors

YAML Configuration File

You can configure all of your monitors using a single YAML file. This can be version controlled and synced to Cronitor as part of a deployment or build process. For details on all of the attributes that can be set, see the Monitor API documentation.

using Cronitor;

// your api keys can found here - https://cronitor.io/settings/api
var cronitor = new CronitorClient(new CronitorOptions { ApiKey = "apiKey123" });

cronitor.ReadConfig("./cronitor.yaml"); // parse the yaml file of monitors

await cronitor.ValidateConfigAsync(); // send monitors to Cronitor for configuration validation

await cronitor.ApplyConfigAsync(); // sync the monitors from the config file to Cronitor

await cronitor.GenerateConfigAsync(); // generate a new config file from the Cronitor API

The timeout value for ValidateConfigAsync, ApplyConfigAsync and GenerateConfigAsync is 10 seconds by default. The value can be rewritten by setting the environment variable CRONITOR_TIMEOUT (seconds). It can also be rewritten by assigning a value to CronitorOptions.Timeout.

cronitor.Options.Timeout = TimeSpan.FromSeconds(30);
await cronitor.ApplyConfigAsync();

The cronitor.yaml file includes three top level keys jobs, checks, heartbeats. You can configure monitors under each key by defining monitors.

jobs:
    nightly-database-backup:
        schedule: 0 0 * * *
        notify:
            - devops-alert-pagerduty
        assertions:
            - metric.duration < 5 minutes

    send-welcome-email:
        schedule: every 10 minutes
        assertions:
            - metric.count > 0
            - metric.duration < 30 seconds

checks:
    cronitor-homepage:
        request:
            url: https://cronitor.io
            regions:
                - us-east-1
                - eu-central-1
                - ap-northeast-1
        assertions:
            - response.code = 200
            - response.time < 2s

    cronitor-ping-api:
        request:
            url: https://cronitor.link/ping
        assertions:
            - response.body contains ok
            - response.time < .25s

heartbeats:
    production-deploy:
        notify:
            alerts: ['deploys-slack']
            events: true # send alert when the event occurs

Async Uploads

If you are working with large YAML files (300+ monitors), you may hit timeouts when trying to sync monitors in a single http request. This workload to be processed asynchronously by adding the key async: true to the config file. The request will immediately return a batch_key. If a webhook_url parameter is included, Cronitor will POST to that URL with the results of the background processing and will include the batch_key matching the one returned in the initial response.

PutAsync

You can also create and update monitors by calling PutAsync. Monitors can be dictionaries or plain objects (property names are sent in snake_case). For details on all of the attributes that can be set see the Monitor API documentation.

using Cronitor;

var monitors = await cronitor.PutAsync(new object[]
{
    new
    {
        Type = "job",
        Key = "send-customer-invoices",
        Schedule = "0 0 * * *",
        Assertions = new[] { "metric.duration < 5 min" },
        Notify = new[] { "devops-alerts-slack" },
    },
    new Dictionary<string, object?>
    {
        ["type"] = "check",
        ["key"] = "Cronitor Homepage",
        ["schedule"] = "every 45 seconds",
        ["request"] = new Dictionary<string, object?> { ["url"] = "https://cronitor.io" },
        ["assertions"] = new[] { "response.code = 200", "response.time < 600ms" },
    },
});

Pass rollback: true to validate the monitors without saving them. A 400 response raises ApiValidationException.

Listing and Inspecting Monitors

You can fetch multiple monitors using ListAsync with optional filtering and pagination:

using Cronitor;

// Fetch specific monitors by key
var monitors = await cronitor.ListAsync(new ListOptions { Keys = new[] { "backup-job", "health-check", "send-invoices" } });

// Fetch all monitors (first page, 100 results by default)
monitors = await cronitor.ListAsync();

// Fetch monitors with filters
monitors = await cronitor.ListAsync(new ListOptions { Type = "check", State = "failing" });

// Fetch a specific page
monitors = await cronitor.ListAsync(new ListOptions { Type = "job", Page = 2, PageSize = 50 });

// Fetch all pages automatically
monitors = await cronitor.ListAsync(new ListOptions { Type = "job", AutoPaginate = true });

// Search monitors
monitors = await cronitor.ListAsync(new ListOptions { Search = "backup" });

After fetching a monitor, access its data through MonitorData. Common fields have typed properties and nested values can be read by path:

using Cronitor;

// Fetch an existing monitor
var monitor = cronitor.Monitor("send-invoices");
var data = await monitor.FetchAsync(); // also available afterwards as monitor.Data

// Access monitor attributes
Console.WriteLine(data.Name);
Console.WriteLine(data.Type);
Console.WriteLine(data.Schedule);

// Access nested data
Console.WriteLine(data.GetString("attributes", "code"));
if (data.Get("latest_event") is { } latest)
{
    Console.WriteLine(data.GetDouble("latest_event.stamp"));
    Console.WriteLine(data.GetString("latest_event.event"));
}

// The raw System.Text.Json document is available too
JsonElement raw = data.Raw;

// Pretty print the entire monitor as JSON
Console.WriteLine(data);

Pausing, Resetting, and Deleting

using Cronitor;

var monitor = cronitor.Monitor("heartbeat-monitor");

await monitor.PauseAsync(24); // pause alerting for 24 hours
await monitor.UnpauseAsync(); // alias for PauseAsync(0)
await monitor.OkAsync(); // manually reset to a passing state alias for monitor.PingAsync(new PingOptions { State = PingState.Ok })
await monitor.DeleteAsync(); // destroy the monitor

Errors

All exceptions derive from CronitorException:

Exception Raised when
MonitorNotFoundException the Monitor API returns 404 for a key
ConfigValidationException no config path is set or the config file cannot be read
ApiValidationException the Monitor API rejects a put with 400
AuthenticationException a call that needs an API key was made without one
CronitorApiException any other unexpected Monitor API response (carries StatusCode)

Package Configuration

The package needs to be configured with your account's API key, which is available on the account settings page. You can also optionally specify an ApiVersion and an Environment. If not provided, your account default is used. These can also be supplied using the environment variables CRONITOR_API_KEY, CRONITOR_API_VERSION, CRONITOR_ENVIRONMENT; CronitorOptions reads them when constructed.

using Cronitor;

// your api keys can found here - https://cronitor.io/settings
var options = new CronitorOptions
{
    ApiKey = "apiKey123",
    ApiVersion = "2020-10-01",
    Environment = "cluster_1_prod",
    Timeout = TimeSpan.FromSeconds(10), // CRONITOR_TIMEOUT
    ConfigPath = "./cronitor.yaml", // CRONITOR_CONFIG
};

var cronitor = new CronitorClient(options);

Per-monitor overrides are available as well: cronitor.Monitor("key", apiKey: "...", apiVersion: "...", environment: "...").

Requests are retried up to 3 times with exponential backoff (0.3s base) on connection errors and 5xx responses. Logging goes through an optional Microsoft.Extensions.Logging.ILogger passed to the client (new CronitorClient(options, logger: logger)). For tests, pass your own HttpMessageHandler and override PingBaseUrl / MonitorApiBaseUrl on the options to point at a local server.

Bring your own HttpClient

You can hand the client an existing HttpClient, for example one from IHttpClientFactory. In that case the client does not add its retry handler; retries only apply if your pipeline includes RetryingHttpMessageHandler:

services.AddHttpClient("cronitor")
    .AddHttpMessageHandler(() => new RetryingHttpMessageHandler(retries: 3, backoff: TimeSpan.FromMilliseconds(300)));

var cronitor = new CronitorClient(httpClientFactory.CreateClient("cronitor"), options);

The default constructor wires the retry handler in for you.

Roadmap

  • Hangfire integration (automatic monitors and pings for recurring jobs).
  • Quartz.NET integration (job listener sending run/complete/fail events).

Contributing

Pull requests and features are happily considered! By participating in this project you agree to abide by the Code of Conduct.

To contribute

Fork, then clone the repo:

git clone git@github.com:your-username/cronitor-dotnet.git

Set up your machine (requires the .NET 8 SDK):

dotnet restore

Make sure the tests pass:

dotnet test

Optional: Run integration tests against the real Cronitor API:

export CRONITOR_TEST_API_KEY=your_api_key_here
dotnet test

Make your change. Add tests for your change. Make the tests pass:

dotnet format --verify-no-changes
dotnet test

Push to your fork and submit a pull request

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 97 9/10/2026
1.0.0 94 9/9/2026