---
title: Example Tasks
description: Practical DurableStack job examples for common application workloads.
order: 61
available_in: [dotnet, nodejs]
---

# Example Tasks

These examples show common task shapes you can adapt quickly.

## Email notification job (typed payload)

:::code-group
```csharp
using DurableStack.Core;
using DurableStack.Core.Abstractions;
using DurableStack.Hosting.DependencyInjection;

[DurableJob(Name = "send-welcome-email", MaxAttempts = 5)]
public sealed class SendWelcomeEmailJob : IDurableJob<SendWelcomeEmailArgs>
{
    private readonly ILogger<SendWelcomeEmailJob> _logger;

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

    public Task ExecuteAsync(SendWelcomeEmailArgs args, JobContext context, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Sending welcome email to {Email}. RunId={RunId}", args.Email, context.RunId);
        return Task.CompletedTask;
    }
}

public sealed class SendWelcomeEmailArgs
{
    public string Email { get; set; } = string.Empty;
}
```

```typescript
type SendWelcomeEmailArgs = {
  email: string;
};

runtime.registerJob<SendWelcomeEmailArgs>("send-welcome-email", async (args, context) => {
  console.log(`Sending welcome email to ${args.email}. RunId=${context.runId}`);
});
```
:::

Enqueue:

:::code-group
```csharp
var runId = await durableClient.EnqueueAsync<SendWelcomeEmailJob>(new SendWelcomeEmailArgs
{
    Email = "hello@example.com"
}, cancellationToken);
```

```typescript
const runId = await runtime.enqueue<SendWelcomeEmailArgs>("send-welcome-email", {
  email: "hello@example.com"
});
```
:::

## Recurring cleanup job

:::code-group
```csharp
[DurableJob(Name = "cleanup-expired-sessions", MaxAttempts = 3)]
[RecurringJob("0 */1 * * *", TimeZone = "UTC", AllowConcurrentRuns = false)]
public sealed class CleanupExpiredSessionsJob : IDurableJob
{
    public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
    {
        return Task.CompletedTask;
    }
}
```

```typescript
runtime.registerRecurring(
  "cleanup-expired-sessions",
  "0 */1 * * *",
  "UTC",
  async () => {
    console.log("Cleaning up expired sessions");
  },
  {
    maxAttempts: 3,
    allowConcurrentRuns: false
  }
);
```
:::

## Third-party sync job with backoff

:::code-group
```csharp
[DurableJob(
    Name = "sync-third-party-orders",
    MaxAttempts = 6,
    RetryBehavior = RetryBehavior.Backoff,
    RetryInitialDelaySeconds = 15)]
[RecurringJob("*/10 * * * *", TimeZone = "UTC", AllowConcurrentRuns = false)]
public sealed class ThirdPartyOrderSyncJob : IDurableJob
{
    public async Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
    {
        await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken);
    }
}
```

```typescript
runtime.registerRecurring(
  "sync-third-party-orders",
  "*/10 * * * *",
  "UTC",
  async () => {
    await new Promise((resolve) => setTimeout(resolve, 50));
  },
  {
    maxAttempts: 6,
    retryBehavior: "Backoff",
    retryInitialDelaySeconds: 15,
    allowConcurrentRuns: false
  }
);
```
:::

## Guidance

- Use typed jobs when payload shape is part of the contract.
- Prefer non-overlapping recurring jobs for mutable resources.
- Use retry backoff for external dependency instability.
