---
title: First Job Example
description: Detailed first-job walkthrough including attribute options and enqueue versus schedule behavior.
order: 14
---

# First Job Example

This walkthrough shows a realistic first job, all options in both job attributes, and why DurableStack separates enqueue metadata from recurring schedule metadata.

## Example job

```csharp
using DurableStack.Core;
using DurableStack.Core.Abstractions;
using DurableStack.Core.Models;
using DurableStack.Hosting.DependencyInjection;

[DurableJob(
    Name = "invoice-sync",
    MaxAttempts = 5,
    RetryBehavior = RetryBehavior.Backoff,
    RetryInitialDelaySeconds = 10)]
[RecurringJob(
    "*/5 * * * *",
    TimeZone = "UTC",
    Enabled = true,
    AllowConcurrentRuns = false)]
public sealed class InvoiceSyncJob : IDurableJob
{
    private readonly ILogger<InvoiceSyncJob> _logger;

    public InvoiceSyncJob(ILogger<InvoiceSyncJob> logger)
    {
        _logger = logger;
    }

    public async Task ExecuteAsync(JobContext context, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "Invoice sync run {RunId} attempt {Attempt} started at {StartedUtc}",
            context.RunId,
            context.Attempt,
            context.StartedAtUtc);

        await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);

        _logger.LogInformation("Invoice sync run {RunId} completed", context.RunId);
    }
}
```

## Why there are two attributes

### `DurableJob` defines execution behavior

- `Name`: stable job identity used for registration and operations.
- `MaxAttempts`: total attempts including initial execution.
- `RetryBehavior`: `FixedDelay` or `Backoff`.
- `RetryInitialDelaySeconds`: per-job retry baseline.

This attribute defines what happens when a run executes and fails.

### `RecurringJob` defines schedule behavior

- `Cron`: schedule cadence.
- `TimeZone`: IANA timezone used for cron evaluation.
- `Enabled`: whether schedule emits new runs.
- `AllowConcurrentRuns`: whether overlapping scheduled runs are allowed.

This attribute defines whether and how new runs are created over time.

## Enqueue versus schedule

- Enqueue creates one run immediately.
- Recurring schedule materializes runs automatically when cron slots are due.
- A job can be enqueue-only (no `RecurringJob`) or recurring (both attributes).

## Enqueue the same job manually

```csharp
using DurableStack.Core.Abstractions;

app.MapPost("/jobs/invoice-sync/run-now", async (
    IDurableStackClient client,
    CancellationToken cancellationToken) =>
{
    var runId = await client.EnqueueAsync<InvoiceSyncJob>(cancellationToken: cancellationToken);
    return Results.Accepted($"/runs/{runId}", new { runId });
});
```

With this route plus `[RecurringJob]`, the same handler supports both:

- automatic recurring execution
- manual run-now execution

## Practical guidance

- Keep `DurableJob.Name` stable after production adoption.
- Use `AllowConcurrentRuns = false` for jobs that mutate shared resources.
- Prefer `RetryBehavior.Backoff` for third-party API dependencies.
- Keep handlers idempotent so retries are safe.

## Next step

After this page, review [Getting Started](getting-started) for the recommended runtime configuration baseline.
