Beacon .NET SDK

The Beacon .NET SDK provides native integration for desktop (WPF, WinForms, MAUI), server (ASP.NET Core, Windows Services), and console applications.

Installation

Install via NuGet:

dotnet add package SoftAgility.Beacon

Or via the Package Manager Console:

Install-Package SoftAgility.Beacon

System Requirements

  • .NET 6, 7, 8, 9, or 10, .NET Framework 4.8, or any .NET Standard 2.0 consumer (as of SDK v3.2.0). Earlier SDK versions (3.0.0–3.1.0) require .NET 8+.
  • Windows, Linux, or macOS (the .NET Framework 4.8 target is Windows-only)
  • No additional dependencies

v3.2.0: Broadened target frameworks to net8.0;net8.0-windows;net6.0;netstandard2.0;net48 — the SDK now runs on .NET Framework 4.8 and .NET Standard 2.0 in addition to modern .NET, with no public-API or wire-format change. See the CHANGELOG.

v3.0.0 (breaking change): The config fields were renamed — AppNameProduct (v2.0.0) and AppVersionProductVersion (v3.0.0) — and the wire fields source_appproduct and source_versionproduct_version. Upgrading from 1.x: rename options.AppNameoptions.Product and options.AppVersionoptions.ProductVersion. Values are unchanged.

Configuration

ASP.NET Core / Host Builder

Register Beacon with dependency injection to resolve IBeaconTracker:

services.AddBeacon(options =>
{
    options.ApiKey = "your-api-key";
    options.ApiBaseUrl = "https://api.beacon.softagility.com";
    options.Product = "MyApp";
    options.ProductVersion = "1.2.0";
});

Manual Initialization

For applications without dependency injection, use the static Configure method. It returns void and creates a singleton BeaconTracker. Retrieve the instance via BeaconTracker.Instance after configuration:

BeaconTracker.Configure(options =>
{
    options.ApiKey = "your-api-key";
    options.ApiBaseUrl = "https://api.beacon.softagility.com";
    options.Product = "MyApp";
    options.ProductVersion = "1.2.0";
});
 
var tracker = BeaconTracker.Instance!;

Calling Configure a second time throws InvalidOperationException. If required options (ApiKey, ApiBaseUrl, Product) are missing, the SDK disables itself silently and logs a warning — it does not throw.

Configuration Options

OptionTypeDefaultDescription
ApiKeystringYour Beacon API key (required)
ApiBaseUrlstringBeacon API base URL (required)
ProductstringYour product’s slug, sent as the product field on every event. Must match a product slug registered in the Beacon dashboard, or events are rejected with UNRECOGNIZED_PRODUCT. The SDK truncates values longer than 128 chars before sending. (required)
ProductVersionstringYour application’s version string, sent as product_version. The SDK truncates values longer than 256 chars; note the backend rejects product_version values longer than 128 chars, so practically keep this under 128. (required)
EnabledbooltrueEnable or disable tracking globally
FlushIntervalSecondsint60How often to send batched events (1-3600)
MaxBatchSizeint25Events per batch. Values above 1000 are clamped to 1000; otherwise the configured value is used.
MaxQueueSizeMbint10Maximum offline queue size in MB (1-1000)
MaxBreadcrumbsint25Breadcrumb ring buffer size (0-200)
EventsEventDefinitionBuilderBuilder for registering known events
LoggerMicrosoft.Extensions.Logging.ILogger?nullOptional logger for SDK diagnostics

Known limit mismatch: The .NET SDK truncates Product at 128 chars and ProductVersion at 256 chars. The wire format actually allows the opposite — product up to 256 and product_version up to 128. To stay safe, keep Product ≤ 128 and ProductVersion ≤ 128.

Identifying Users

Before tracking events or sessions, identify the current actor (user or device):

tracker.Identify("user-123");

Actor IDs can be up to 512 characters. Once identified, all subsequent Track, StartSession, and TrackException calls use this actor. You can also pass an explicit actor ID to any tracking method.

Account & License Context

For B2B vendors who want customer-organisation analytics, the SDK exposes typed methods to attach an account_id and / or license_id to every subsequent event, session start, and exception report:

tracker.Identify("user-123");
tracker.SetAccount("acct_acme");                 // customer organisation
tracker.SetLicense("sub_acme_team_annual");      // contract / entitlement
tracker.StartSession();
tracker.Track("reports", "report_exported");     // event carries both IDs

Behaviour:

  • Persistence. Each call stores the trimmed value in memory and applies it to all future payloads until cleared or Reset() is called.
  • Validation. Each method accepts 1-256 chars; null, empty, whitespace-only, control-char-containing, or oversized values throw ArgumentException. Silent no-op on disabled / disposed trackers.
  • Clearing. ClearAccount() and ClearLicense() clear only their own field. Reset() clears actor, session, account, license, queue, and breadcrumbs.
  • Per-call payload. When set, account_id / license_id are included as top-level JSON fields. When unset, the fields are omitted entirely — the backend distinguishes “not present” from “present but invalid”.
  • No persistence across restarts. Context lives in process memory only. After a restart, your application must call SetAccount / SetLicense again — typically right after the login or license-activation flow that produced the IDs.
  • Mid-session. Calling SetAccount mid-session does NOT mutate the existing session row; the new value applies to events fired after the call. SDK best practice: end the active session and start a new one when the customer / license actually changes (e.g., a user switching workspaces in a multi-tenant client).

Modeling licenses

Prefer per-contract license IDs (one ID shared across every user of a subscription / site / bundle):

tracker.SetLicense("sub_acme_team_annual");     // recommended

Per-user / per-seat license IDs work too, but Beacon’s License Detail page becomes thin under that pattern. See the Licenses section of the user manual for full guidance.

Account- and license-level analytics in the Beacon dashboard are gated to Business and Enterprise plans (Trial also during evaluation). Ingestion itself is tier-blind — your account_id / license_id are stored on every event regardless of plan, so the data is immediately queryable the moment you upgrade.

Tracking Events

Events are organized by category and name, with optional properties.

Basic Event

tracker.Track("ui", "button_clicked", new
{
    button_name = "export",
    screen = "dashboard"
});

With Explicit Actor

tracker.Track("reports", "report_exported", "user-456", new
{
    format = "pdf",
    row_count = 1500
});

Event Limits

  • Category: max 128 characters
  • Name: max 256 characters
  • Actor ID: max 512 characters

Session Management

Sessions group related events and track duration.

// Start a session (uses the identified actor)
tracker.StartSession();
 
// Or start with an explicit actor ID
tracker.StartSession("user-123");
 
// End the current session
tracker.EndSession();

Exception Tracking

Track handled or unhandled exceptions with full stack traces. The SDK sends the exception type, message, and stack trace to the Beacon API, which assigns a fingerprint and groups similar exceptions together.

try
{
    ProcessOrder(order);
}
catch (Exception ex)
{
    tracker.TrackException(ex, ExceptionSeverity.NonFatal);
    throw;
}

Severity options are ExceptionSeverity.Fatal and ExceptionSeverity.NonFatal. Recent tracking calls are automatically attached as breadcrumbs to provide context.

The SDK maintains a circular ring buffer of recent Track calls. When an exception is reported, these breadcrumbs are attached automatically, giving you a timeline of what happened before the error.

Configure the buffer size with MaxBreadcrumbs (default 25, max 200). Set to 0 to disable.

Flushing Events

Events are batched and sent on a timer (FlushIntervalSeconds) or when the batch reaches MaxBatchSize. To flush manually:

await tracker.FlushAsync();

Check the last flush result:

FlushStatus status = tracker.LastFlushStatus;

Offline Persistence

The SDK queues events in a local SQLite database under the OS-specific Local Application Data directory (e.g., %LOCALAPPDATA%\SoftAgility\Beacon\{SanitizedProduct}\queue.db on Windows; equivalent paths on Linux and macOS). The product value is sanitized for safe filesystem use. Events persist across application restarts and sync automatically when connectivity returns.

  • FIFO queue with a configurable maximum size (MaxQueueSizeMb)
  • Best-effort durable retry: events are retained on disk through transient network failures, but if the queue reaches the configured size cap, the oldest events are evicted to make room for new ones
  • Background timer handles automatic flushing
  • Permanent failures (4xx errors other than 401/402/429) are not retried; events are dropped with a logged warning

To guarantee delivery before shutdown, call await tracker.FlushAsync() explicitly. Disposing the tracker persists in-memory events to disk for the next launch but does not perform a network flush.

Event Manifest

Export a JSON manifest of your registered events for import into the Beacon portal’s allowlist page:

tracker.ExportEventManifest("events.json");

Thread Safety

BeaconTracker is fully thread-safe. A single instance can be shared across your application and called from any thread.

Disposal

BeaconTracker implements both IDisposable and IAsyncDisposable. Dispose when your application shuts down to release resources and persist in-memory events to disk for the next launch:

// To deliver pending events to the network before shutdown:
await tracker.FlushAsync();
 
// Then release resources:
tracker.Dispose();
// or
await tracker.DisposeAsync();

Disposal alone does not perform a network flush — pending events are written to disk and sent on the next session. For immediate delivery at shutdown, call FlushAsync first.

WPF Example

public partial class App : Application
{
    private IBeaconTracker _tracker;
 
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
 
        BeaconTracker.Configure(options =>
        {
            options.ApiKey = "your-api-key";
            options.ApiBaseUrl = "https://api.beacon.softagility.com";
            options.Product = "MyDesktopApp";
            options.ProductVersion = "2.1.0";
        });
        _tracker = BeaconTracker.Instance!;
 
        _tracker.Identify("device-" + Environment.MachineName);
        _tracker.StartSession();
    }
 
    protected override async void OnExit(ExitEventArgs e)
    {
        _tracker.EndSession();
        await _tracker.FlushAsync();
        _tracker.Dispose();
        base.OnExit(e);
    }
}

Next Steps