Jobcelis 1.4.1

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

Jobcelis

Official .NET SDK for the Jobcelis Event Infrastructure Platform.

All API calls go to https://jobcelis.com by default -- you only need your API key to get started.

Installation

dotnet add package Jobcelis

Quick Start

using Jobcelis;

// Only your API key is required -- connects to https://jobcelis.com automatically
var client = new JobcelisClient("your_api_key");

var evt = await client.SendEventAsync("order.created", new { order_id = "123", amount = 99.99 });
var webhooks = await client.ListWebhooksAsync();

Custom URL: If you're self-hosting Jobcelis, you can override the base URL:

var client = new JobcelisClient("your_api_key", baseUrl: "https://your-instance.example.com");

Authentication

The auth methods do not require an API key. Use them to register, log in, and manage JWT tokens.

using Jobcelis;

var client = new JobcelisClient("");

// Register a new account
var user = await client.RegisterAsync("alice@example.com", "SecurePass123!", name: "Alice");

// Log in -- returns JWT access token and refresh token
var session = await client.LoginAsync("alice@example.com", "SecurePass123!");
var accessToken = session.GetProperty("token").GetString()!;
var refreshToken = session.GetProperty("refresh_token").GetString()!;

// Set the JWT for subsequent authenticated calls
client.SetAuthToken(accessToken);

// Refresh an expired token
var newSession = await client.RefreshTokenAsync(refreshToken);
client.SetAuthToken(newSession.GetProperty("token").GetString()!);

// Verify MFA (requires auth token already set)
var result = await client.VerifyMfaAsync(accessToken, "123456");

Events

// Send a single event
var evt = await client.SendEventAsync("order.created", new { order_id = "123", amount = 99.99 });

// Send batch events (up to 1000)
var batch = await client.SendEventsAsync(new[]
{
    new { topic = "order.created", payload = new { order_id = "1" } },
    new { topic = "order.created", payload = new { order_id = "2" } },
});

// List events with pagination
var events = await client.ListEventsAsync(limit: 25);

// Get / delete a single event
var detail = await client.GetEventAsync("evt_abc123");
await client.DeleteEventAsync("evt_abc123");

Simulate

// Dry-run an event to see which webhooks would fire
var result = await client.SimulateEventAsync("order.created", new { order_id = "test" });

Webhooks

// Create a webhook
var webhook = await client.CreateWebhookAsync("https://example.com/webhook",
    new() { ["topics"] = new[] { "order.*" } });

// List, get, update, delete
var webhooks = await client.ListWebhooksAsync();
var wh = await client.GetWebhookAsync("wh_abc123");
await client.UpdateWebhookAsync("wh_abc123", new { url = "https://new-url.com/hook" });
await client.DeleteWebhookAsync("wh_abc123");

// Health and templates
var health = await client.WebhookHealthAsync("wh_abc123");
var templates = await client.WebhookTemplatesAsync();

Deliveries

var deliveries = await client.ListDeliveriesAsync(limit: 20, status: "failed");
await client.RetryDeliveryAsync("del_abc123");

Dead Letters

var deadLetters = await client.ListDeadLettersAsync();
var dl = await client.GetDeadLetterAsync("dlq_abc123");
await client.RetryDeadLetterAsync("dlq_abc123");
await client.ResolveDeadLetterAsync("dlq_abc123");

Replays

var replay = await client.CreateReplayAsync(
    "order.created",
    "2026-01-01T00:00:00Z",
    "2026-01-31T23:59:59Z",
    webhookId: "wh_abc123"  // optional
);
var replays = await client.ListReplaysAsync();
var r = await client.GetReplayAsync("rpl_abc123");
await client.CancelReplayAsync("rpl_abc123");

Scheduled Jobs

// Create a job
var job = await client.CreateJobAsync("daily-report", "default", "0 9 * * *",
    new() { ["payload"] = new { type = "daily" } });

// CRUD
var jobs = await client.ListJobsAsync(limit: 10);
var j = await client.GetJobAsync("job_abc123");
await client.UpdateJobAsync("job_abc123", new { cron_expression = "0 10 * * *" });
await client.DeleteJobAsync("job_abc123");

// List runs for a job
var runs = await client.ListJobRunsAsync("job_abc123", limit: 20);

// Preview cron schedule
var preview = await client.CronPreviewAsync("0 9 * * *", count: 10);

Pipelines

var pipeline = await client.CreatePipelineAsync("order-processing",
    new[] { "order.created" },
    new object[]
    {
        new { type = "filter", config = new { field = "amount", gt = 100 } },
        new { type = "transform", config = new { add_field = "priority", value = "high" } },
    }
);

var pipelines = await client.ListPipelinesAsync();
var p = await client.GetPipelineAsync("pipe_abc123");
await client.UpdatePipelineAsync("pipe_abc123", new { name = "order-processing-v2" });
await client.DeletePipelineAsync("pipe_abc123");

// Test a pipeline with a sample payload
var testResult = await client.TestPipelineAsync("pipe_abc123", new
{
    topic = "order.created",
    payload = new { id = "1" }
});

Event Schemas

var schema = await client.CreateEventSchemaAsync("order.created", new
{
    type = "object",
    properties = new
    {
        order_id = new { type = "string" },
        amount = new { type = "number" },
    },
    required = new[] { "order_id", "amount" },
});

var schemas = await client.ListEventSchemasAsync();
var s = await client.GetEventSchemaAsync("sch_abc123");
await client.UpdateEventSchemaAsync("sch_abc123", new { schema = new { type = "object" } });
await client.DeleteEventSchemaAsync("sch_abc123");

// Validate a payload against a topic's schema
var valid = await client.ValidatePayloadAsync("order.created", new { order_id = "123", amount = 50 });

Sandbox

// Create a temporary endpoint for testing
var endpoint = await client.CreateSandboxEndpointAsync(name: "my-test");
var endpoints = await client.ListSandboxEndpointsAsync();

// Inspect received requests
var requests = await client.ListSandboxRequestsAsync("sbx_abc123", limit: 20);

await client.DeleteSandboxEndpointAsync("sbx_abc123");

Analytics

var eventsChart = await client.EventsPerDayAsync(days: 30);
var deliveriesChart = await client.DeliveriesPerDayAsync(days: 7);
var topics = await client.TopTopicsAsync(limit: 5);
var stats = await client.WebhookStatsAsync();

Project and Token Management

// Current project
var project = await client.GetProjectAsync();
await client.UpdateProjectAsync(new { name = "My Project v2" });

// Topics
var topics = await client.ListTopicsAsync();

// API token
var token = await client.GetTokenAsync();
var newToken = await client.RegenerateTokenAsync();

// Limit the token to scopes and client IPs (empty allowedIps = any IP)
await client.UpdateTokenRestrictionsAsync(new[] { "events:read" }, new[] { "203.0.113.10" });

Multi-Project Management

var projects = await client.ListProjectsAsync();
var newProject = await client.CreateProjectAsync("staging-env");
var proj = await client.GetProjectByIdAsync("proj_abc123");
await client.UpdateProjectByIdAsync("proj_abc123", new { name = "production-env" });
await client.SetDefaultProjectAsync("proj_abc123");
await client.DeleteProjectAsync("proj_abc123");

Team Members

var members = await client.ListMembersAsync("proj_abc123");
var member = await client.AddMemberAsync("proj_abc123", "alice@example.com", role: "admin");
await client.UpdateMemberAsync("proj_abc123", "mem_abc123", "viewer");
await client.RemoveMemberAsync("proj_abc123", "mem_abc123");

Invitations

// List pending invitations
var invitations = await client.ListPendingInvitationsAsync();

// Accept or reject
await client.AcceptInvitationAsync("inv_abc123");
await client.RejectInvitationAsync("inv_def456");

Audit Logs

var logs = await client.ListAuditLogsAsync(limit: 100);

Data Export

Export methods return raw strings (CSV or JSON).

// Export as CSV
var csvData = await client.ExportEventsAsync(format: "csv");
File.WriteAllText("events.csv", csvData);

// Export as JSON
var jsonData = await client.ExportDeliveriesAsync(format: "json");

// Other exports
await client.ExportJobsAsync(format: "csv");
await client.ExportAuditLogAsync(format: "csv");

GDPR / Privacy

// Consent management
var consents = await client.GetConsentsAsync();
await client.AcceptConsentAsync("marketing");

// Data portability
var myData = await client.ExportMyDataAsync();

// Processing restrictions
await client.RestrictProcessingAsync();
await client.LiftRestrictionAsync();

// Right to object
await client.ObjectToProcessingAsync();
await client.RestoreConsentAsync();

Health Check

var health = await client.HealthAsync();
var status = await client.StatusAsync();

Retries

The client retries failed requests automatically, so a short outage (for example while the API is being deployed) does not lose your events:

  • Network errors (connection refused or reset, DNS or TLS failures, timeouts) and HTTP 429, 502, 503 and 504 are retried.
  • Up to 6 retries by default, with exponential backoff (1 s, 2 s, 4 s, 8 s, 16 s, 30 s, plus or minus 20 % jitter): about 60 seconds in total. A Retry-After header from the server is honoured (up to 60 s).
  • POST requests are retried on 429/502/503/504, but after a network error only when they carry an idempotency key, because the server may already have processed them. Other errors (400, 404, 500, ...) are never retried and throw JobcelisException as usual.
  • Events are always safe to retry: SendEventAsync sends a random idempotency key in the X-Idempotency-Key header (the same key on every retry), and SendEventsAsync adds an idempotency_key to every event that does not have one. To use your own key:
await client.SendEventAsync("order.created", new { order_id = "123" }, idempotencyKey: "order-123");

Change or disable retries with maxRetries:

var client = new JobcelisClient("your_api_key", maxRetries: 3);  // at most 3 retries
var noRetries = new JobcelisClient("your_api_key", maxRetries: 0);  // a single attempt

Error Handling

using Jobcelis;

var client = new JobcelisClient("your_api_key");

try
{
    var evt = await client.GetEventAsync("nonexistent");
}
catch (JobcelisException ex)
{
    Console.WriteLine($"Status: {ex.StatusCode}");  // 404
    Console.WriteLine($"Detail: {ex.Detail}");
}

Webhook Signature Verification

using Jobcelis;

// In an ASP.NET Core controller
[HttpPost("webhook")]
public async Task<IActionResult> HandleWebhook()
{
    using var reader = new StreamReader(Request.Body);
    var body = await reader.ReadToEndAsync();
    var signature = Request.Headers["X-Signature"].FirstOrDefault() ?? "";

    if (!WebhookVerifier.Verify("your_webhook_secret", body, signature))
        return Unauthorized("Invalid signature");

    // Process the event...
    return Ok();
}

License

BSL-1.1 (Business Source License)

Releasing

Merge to main: the Release workflow publishes every change to the package as the next patch version (1.3.1 → 1.3.2) to NuGet, writes the version into src/Jobcelis/Jobcelis.csproj and tags the commit. Changes to docs, tests or CI publish nothing. For a minor or major version, raise it in src/Jobcelis/Jobcelis.csproj in the same pull request. Every pull request runs CI.

Publishing uses NuGet Trusted Publishing: the workflow exchanges GitHub's identity for a short-lived API key on each run, so no NuGet API key is stored in the repository. The nuget.org policy must point to owner jobcelis-team, repository jobcelis-dotnet, workflow release.yml, package Jobcelis.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

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.4.1 78 11/9/2026
1.4.0 83 11/9/2026
1.3.0 144 10/18/2026
1.2.1 160 3/10/2026
1.2.0 129 3/10/2026
1.0.0 126 3/7/2026