IJobClient Interface
The primary API for enqueuing and managing background jobs from your application code. Inject it anywhere — controllers, services, middleware, Razor Pages, or Minimal API endpoints.
Namespace: Zeridion.Flare · Assembly: Zeridion.Flare.dll
public interface IJobClient
{
Task<string> EnqueueAsync<TJob>(object payload, JobOptions? options = null, CancellationToken ct = default)
where TJob : class;
Task<string> ScheduleAsync<TJob>(object payload, TimeSpan delay, JobOptions? options = null, CancellationToken ct = default)
where TJob : class;
Task<string> ScheduleAsync<TJob>(object payload, DateTimeOffset runAt, JobOptions? options = null, CancellationToken ct = default)
where TJob : class;
Task<string> ContinueWithAsync<TJob>(string parentJobId, object payload, JobOptions? options = null, CancellationToken ct = default)
where TJob : class;
Task<bool> CancelAsync(string jobId, CancellationToken ct = default);
Task<bool> RetryAsync(string jobId, CancellationToken ct = default);
Task<JobStatus?> GetStatusAsync(string jobId, CancellationToken ct = default);
Task<ListJobsResult> ListAsync(ListJobsOptions? options = null, CancellationToken ct = default);
}
Every async method takes an optional CancellationToken as its last parameter — pass HttpContext.RequestAborted or any token to abort the in-flight HTTP request on caller cancellation.
Methods overview
| Method | Returns | Description |
|---|---|---|
EnqueueAsync | Task<string> | Enqueue a job for immediate execution. |
ScheduleAsync (×2) | Task<string> | Enqueue a job to run after a TimeSpan delay or at a DateTimeOffset. Two overloads. |
ContinueWithAsync | Task<string> | Enqueue a job that runs after a parent succeeds. |
CancelAsync | Task<bool> | Cancel a pending or scheduled job. |
RetryAsync | Task<bool> | Retry a failed or dead-lettered job. |
GetStatusAsync | Task<JobStatus?> | Get the current status of a job. |
ListAsync | Task<ListJobsResult> | List jobs with optional filters + cursor pagination. |
EnqueueAsync
Task<string> EnqueueAsync<TJob>(object payload, JobOptions? options = null)
where TJob : class;
Enqueue a job for immediate execution. The job enters the Pending state and becomes eligible for the next available worker.
| Parameter | Type | Description |
|---|---|---|
TJob | type param | The job class. Must implement IJob<TPayload>. |
payload | object | The payload to pass to the job. Serialized to JSON. |
options | JobOptions? | Optional per-call overrides for queue, retries, etc. |
| Returns | string | The job ID assigned by the server. |
var jobId = await jobs.EnqueueAsync<SendWelcomeEmail>(
new NewUserEvent { Email = "alice@example.com", Name = "Alice" });
The job type is resolved via the SDK's job-type catalog. If TJob was not discovered during assembly scanning, an exception is thrown.
ScheduleAsync (TimeSpan)
Task<string> ScheduleAsync<TJob>(object payload, TimeSpan delay, JobOptions? options = null)
where TJob : class;
Enqueue a job that becomes eligible for processing after the specified delay. The job enters the Scheduled state and transitions to Pending when UtcNow + delay is reached.
| Parameter | Type | Description |
|---|---|---|
delay | TimeSpan | How long to wait before the job becomes eligible. |
var jobId = await jobs.ScheduleAsync<SendFollowUpEmail>(
new FollowUpPayload { UserId = userId },
TimeSpan.FromHours(24));
ScheduleAsync (DateTimeOffset)
Task<string> ScheduleAsync<TJob>(object payload, DateTimeOffset runAt, JobOptions? options = null)
where TJob : class;
Enqueue a job that becomes eligible for processing at a specific UTC time. Use this when you know the exact time a job should run.
| Parameter | Type | Description |
|---|---|---|
runAt | DateTimeOffset | The UTC time when the job becomes eligible for processing. |
var jobId = await jobs.ScheduleAsync<PublishArticle>(
new PublishPayload { ArticleId = article.Id },
article.PublishAt);
ContinueWithAsync
Task<string> ContinueWithAsync<TJob>(string parentJobId, object payload, JobOptions? options = null)
where TJob : class;
Enqueue a continuation job that runs after a parent job succeeds. The child job enters the Scheduled state and transitions to Pending only when the parent reaches Succeeded.
| Parameter | Type | Description |
|---|---|---|
parentJobId | string | The ID of the parent job to wait for. Must be non-null. |
Parent outcome behavior:
| Parent state | Child result |
|---|---|
Succeeded | Child transitions from Scheduled to Pending |
Failed | Child stays in Scheduled (parent may still retry) |
DeadLetter | Child is cancelled (cascading cancellation) |
Cancelled | Child is cancelled (cascading cancellation) |
var processId = await jobs.EnqueueAsync<ProcessOrder>(orderPayload);
var emailId = await jobs.ContinueWithAsync<SendConfirmationEmail>(
processId,
new EmailPayload { OrderId = order.Id, Email = order.Email });
You can chain multiple continuations from the same parent, or build multi-step pipelines by chaining continuations in sequence:
var step1 = await jobs.EnqueueAsync<ValidateOrder>(payload);
var step2 = await jobs.ContinueWithAsync<ChargePayment>(step1, chargePayload);
var step3 = await jobs.ContinueWithAsync<FulfillOrder>(step2, fulfillPayload);
var step4 = await jobs.ContinueWithAsync<SendReceipt>(step3, receiptPayload);
The parentJobId must reference an existing job. If the parent is already in a terminal state (Succeeded, DeadLetter, or Cancelled), the continuation is handled immediately — activated if the parent succeeded, or cancelled otherwise. If the parent is in Failed state, the child remains Scheduled because the parent may still be retried.
CancelAsync
Task<bool> CancelAsync(string jobId, CancellationToken ct = default);
Cancel a pending or scheduled job.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to cancel. |
ct | CancellationToken | Optional. Aborts the in-flight HTTP request when triggered. |
| Returns | bool | true if cancelled successfully. |
Returns true if the job was successfully cancelled. Returns false if the job is in a non-cancellable state; the API returns 409 Conflict, and the SDK returns false instead of throwing, making it safe to call without try/catch. Throws FlareNotFoundException if the job does not exist (404).
var cancelled = await jobs.CancelAsync(jobId);
if (!cancelled)
{
// Job is already processing or completed — can't cancel (409)
}
Cancelling a parent job also cascades to any child continuation jobs in the Scheduled state.
RetryAsync
Task<bool> RetryAsync(string jobId, CancellationToken ct = default);
Retry a failed or dead-lettered job, resetting it to Pending.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to retry. |
ct | CancellationToken | Optional. Aborts the in-flight HTTP request when triggered. |
| Returns | bool | true if retried successfully. |
Returns true if the job was successfully retried. Returns false if the job is not in a retryable state; the API returns 409 Conflict, and the SDK returns false instead of throwing, making it safe to call without try/catch. Throws FlareNotFoundException if the job does not exist (404).
When retrying a DeadLetter job where AttemptNumber >= MaxAttempts, the server automatically bumps MaxAttempts to allow at least one more attempt.
var retried = await jobs.RetryAsync(jobId);
if (retried)
{
// Job is back in Pending state, will be picked up by next available worker
}
GetStatusAsync
Task<JobStatus?> GetStatusAsync(string jobId, CancellationToken ct = default);
Get the current status of a job.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to check. |
ct | CancellationToken | Optional. Aborts the in-flight HTTP request when triggered. |
| Returns | JobStatus? | The job status, or null if not found. |
Returns a JobStatus object with the job's current state, timing, progress, error information, and tags. Returns null if no job exists with the given ID.
var status = await jobs.GetStatusAsync(jobId);
if (status is not null)
{
Console.WriteLine($"Job {status.JobId} is {status.State}");
if (status.Progress.HasValue)
Console.WriteLine($"Progress: {status.Progress:P0}");
if (status.ErrorMessage is not null)
Console.WriteLine($"Error: {status.ErrorMessage}");
}
ListAsync
Task<ListJobsResult> ListAsync(ListJobsOptions? options = null, CancellationToken ct = default);
List jobs for the authenticated project, with optional filters and cursor-based pagination. Mirrors the wire shape of GET /flare/v1/jobs and is the parity surface for the TypeScript / Python / Ruby SDKs' listJobs.
| Parameter | Type | Description |
|---|---|---|
options | ListJobsOptions? | Filter + pagination criteria. Pass null for an unfiltered first page. |
ct | CancellationToken | Optional. Aborts the in-flight HTTP request when triggered. |
| Returns | ListJobsResult | A page of jobs plus a continuation cursor when more rows exist. |
ListJobsOptions accepts:
| Property | Type | Description |
|---|---|---|
State | JobState? | Restrict to a single state (e.g. JobState.Pending). |
Queue | string? | Restrict to a single queue name. |
JobType | string? | Restrict to a single fully-qualified CLR job type name. |
CreatedAfter | DateTimeOffset? | Inclusive lower bound on CreatedAt. UTC. |
CreatedBefore | DateTimeOffset? | Inclusive upper bound on CreatedAt. UTC. |
Limit | int? | Maximum rows per page. Server-side default applies if null. |
Cursor | string? | Continuation cursor from a prior call's NextCursor. null on the first call. |
RequestId | string? | Client-supplied request ID sent as the X-Request-Id header for log correlation. Optional. |
ListJobsResult returns:
| Property | Type | Description |
|---|---|---|
Items | IReadOnlyList<JobStatus> | The page of jobs. |
HasMore | bool | true if more rows exist beyond this page. |
NextCursor | string? | Continuation cursor; pass to ListJobsOptions.Cursor on the next call. null when no more rows. |
Each item in Items is a JobStatus snapshot — the same shape returned by GetStatusAsync.
// First page: 50 most recent pending jobs on the "email" queue.
var page = await jobs.ListAsync(new ListJobsOptions
{
State = JobState.Pending,
Queue = "email",
Limit = 50
});
foreach (var job in page.Items)
Console.WriteLine($"{job.JobId} {job.State} {job.CreatedAt:O}");
// Walk the cursor until exhausted.
while (page.HasMore)
{
page = await jobs.ListAsync(new ListJobsOptions
{
State = JobState.Pending,
Queue = "email",
Limit = 50,
Cursor = page.NextCursor
});
foreach (var job in page.Items)
Console.WriteLine($"{job.JobId} {job.State} {job.CreatedAt:O}");
}
Keep filter criteria stable across pages — changing State, Queue, or the date range mid-walk invalidates the cursor and the server returns a 400.
Usage example
A complete Minimal API controller using all IJobClient methods:
var app = builder.Build();
app.UseZeridionFlare();
app.MapPost("/orders", async (CreateOrderRequest req, IJobClient jobs) =>
{
var order = await CreateOrder(req);
// Enqueue immediately
var processId = await jobs.EnqueueAsync<ProcessOrder>(
new OrderPayload { OrderId = order.Id },
new JobOptions { Queue = "orders", IdempotencyKey = $"order:{order.Id}" });
// Chain a confirmation email after processing succeeds
await jobs.ContinueWithAsync<SendOrderConfirmation>(
processId,
new EmailPayload { OrderId = order.Id, Email = order.Email });
return Results.Created($"/orders/{order.Id}", new { order_id = order.Id, job_id = processId });
});
app.MapPost("/reports/schedule", async (ReportRequest req, IJobClient jobs) =>
{
// Schedule for a specific time
var jobId = await jobs.ScheduleAsync<GenerateReport>(
new ReportPayload { ReportType = req.Type },
req.RunAt);
return Results.Accepted(value: new { job_id = jobId });
});
app.MapPost("/jobs/{id}/cancel", async (string id, IJobClient jobs) =>
{
var cancelled = await jobs.CancelAsync(id);
return cancelled ? Results.Ok() : Results.Conflict(new { error = "Job cannot be cancelled" });
});
app.MapPost("/jobs/{id}/retry", async (string id, IJobClient jobs) =>
{
var retried = await jobs.RetryAsync(id);
return retried ? Results.Ok() : Results.Conflict(new { error = "Job cannot be retried" });
});
app.MapGet("/jobs/{id}", async (string id, IJobClient jobs) =>
{
var status = await jobs.GetStatusAsync(id);
return status is not null ? Results.Ok(status) : Results.NotFound();
});
app.Run();
Exceptions
EnqueueAsync, ScheduleAsync, and ContinueWithAsync can throw:
FlareAuthenticationException— invalid or missing API key (401)FlareConflictException— idempotency key conflict (409)FlareRateLimitException— rate limit exceeded (429)FlareApiException— any other API error
CancelAsync and RetryAsync return false on 409 (state conflict) instead of throwing. They throw FlareNotFoundException on 404 (job not found).
GetStatusAsync returns null when the job is not found (404) instead of throwing FlareNotFoundException.
See the Exception hierarchy for details and catch patterns.
See also
- JobOptions — per-call overrides accepted by enqueue and schedule methods
- JobStatus — the object returned by
GetStatusAsync - JobState — the lifecycle states and state machine
- Job continuations guide — patterns for chaining jobs
- Error handling guide — handling SDK exceptions