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)
where TJob : class;
Task<string> ScheduleAsync<TJob>(object payload, TimeSpan delay, JobOptions? options = null)
where TJob : class;
Task<string> ScheduleAsync<TJob>(object payload, DateTimeOffset runAt, JobOptions? options = null)
where TJob : class;
Task<string> ContinueWithAsync<TJob>(string parentJobId, object payload, JobOptions? options = null)
where TJob : class;
Task<bool> CancelAsync(string jobId);
Task<bool> RetryAsync(string jobId);
Task<JobStatus?> GetStatusAsync(string jobId);
}
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. |
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 JobTypeRegistry. 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);
Cancel a pending or scheduled job.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to cancel. |
| 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, which the SDK maps to false). 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);
Retry a failed or dead-lettered job, resetting it to Pending.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to retry. |
| 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, which the SDK maps to false). 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);
Get the current status of a job.
| Parameter | Type | Description |
|---|---|---|
jobId | string | The ID of the job to check. |
| 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}");
}
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