.NET SDK

Monitor scheduled jobs from your .NET code with the official SteadyCron SDK. Wrap a job with TrackAsync and get alerted on missed, failed, and stuck runs — the monitor is declared as code in YAML or Terraform.

SteadyCron.Monitoring is the official .NET code-monitoring SDK. Wrap a scheduled job with TrackAsync and SteadyCron will know when it started, succeeded, or failed — and alert you when it doesn’t run on time or starts but never finishes.

The SDK never creates monitors. You declare the heartbeat monitor as code — in a YAML manifest or with the Terraform provider — and reference it from your application by its stable key. The cron schedule, the alert rules, and the instrumentation all live in the same repository.

Install

dotnet add package SteadyCron.Monitoring

Quick start

ASP.NET Core / Generic Host

Register the monitor with dependency injection:

// Program.cs
builder.Services.AddSteadyCron(o =>
{
    o.ApiKey = builder.Configuration["SteadyCron:ApiKey"]; // read-only key
    o.Environment = builder.Environment.EnvironmentName;   // optional
});
// BackupWorker.cs
public class BackupWorker(ISteadyCronMonitor monitor) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        await monitor.TrackAsync("nightly-db-backup", async ct =>
        {
            await RunBackupAsync(ct);
        }, ct);
    }
}

The string "nightly-db-backup" is the same key you set on the steadycron_heartbeat_monitor resource (Terraform) or the heartbeat job id (YAML manifest). That is the only contract between your schedule-as-code and your application.

Console / standalone (no DI)

var monitor = SteadyCronMonitor.Create(new SteadyCronOptions
{
    ApiKey = Environment.GetEnvironmentVariable("STEADYCRON_API_KEY"),
});

await monitor.TrackAsync("nightly-db-backup", ct => RunBackupAsync(ct));

Authentication

The SDK uses a read-only API key to resolve a monitor key to its ping token at startup. Create one under Settings → API keys → New key → Scope: Read-only.

Provide it via any of:

  • Environment variable: STEADYCRON_API_KEY=sc_ro_...
  • SteadyCronOptions.ApiKey directly
  • "SteadyCron:ApiKey" in appsettings.json

Configuration

PropertyDefaultEnv var fallback
ApiKeynullSTEADYCRON_API_KEY
ApiUrlhttps://api.steadycron.comSTEADYCRON_API_URL
PingUrlhttps://ping.steadycron.comSTEADYCRON_PING_URL
EnvironmentnullSTEADYCRON_ENVIRONMENT
CaptureErrorsfalse
PingTimeout5 seconds
ResolveCacheTtl1 hour

How it works

  1. On first use, the SDK calls GET /api/monitors/resolve?key=<your-key> with the read-only API key to retrieve the ping token. The token is cached for one hour (configurable via ResolveCacheTtl).
  2. All pings are fire-and-forget: a bounded timeout (~5 s) is applied; transport errors are logged and swallowed, never propagated.
  3. Resolution errors — 404 (unknown key), 409 (ambiguous key), or wrong kind — raise immediately. They indicate misconfiguration.
  4. On an exception inside TrackAsync, a fail ping is sent and the original exception is rethrown unchanged.

Direct / token mode

If you cannot use API-key resolution (for example, in an air-gapped environment), set the ping token directly and skip the resolve call:

o.Monitors["nightly-db-backup"] = "hRkmWz8oZtlMFzvTAUdnRE";

The token is visible in the dashboard under Job detail → Code monitoring → Reveal ping token.

Reliability contract

  • Ping failures are never propagated. A transport error, timeout, or non-2xx response from a ping is logged at Warning/Debug and discarded.
  • Resolution errors always propagate. A 404 or 409 from the resolve endpoint raises on first use; fix the key or remove the decorator.
  • Original exceptions pass through unchanged. TrackAsync does not wrap exceptions — the fail ping is sent before rethrowing.
  • No ping blocks the job. Pings run inline but are bounded by PingTimeout (default 5 s). A slow ping delays but never hangs the job.