Jobcelis 1.4.1
dotnet add package Jobcelis --version 1.4.1
NuGet\Install-Package Jobcelis -Version 1.4.1
<PackageReference Include="Jobcelis" Version="1.4.1" />
<PackageVersion Include="Jobcelis" Version="1.4.1" />
<PackageReference Include="Jobcelis" />
paket add Jobcelis --version 1.4.1
#r "nuget: Jobcelis, 1.4.1"
#:package Jobcelis@1.4.1
#addin nuget:?package=Jobcelis&version=1.4.1
#tool nuget:?package=Jobcelis&version=1.4.1
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-Afterheader from the server is honoured (up to 60 s). POSTrequests 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 throwJobcelisExceptionas usual.- Events are always safe to retry:
SendEventAsyncsends a random idempotency key in theX-Idempotency-Keyheader (the same key on every retry), andSendEventsAsyncadds anidempotency_keyto 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 | Versions 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. |
-
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.