v0.5.0
.NET SDK

Your first .NET pipeline

Run an HTTP worker, submit a typed stage, inspect its result, and stop cleanly.

Build a console application that starts an HTTP worker, submits a greeting pipeline, waits for its result, and shuts down. Download the example project or follow the steps below. The handler runs in your application; Pipelogiq stores and schedules the work.

Prerequisites

You need a .NET 8 SDK, a running Pipelogiq 0.5.0 server with its migrations applied, and an application API key. Complete the server setup first. Use the external worker API URL, normally http://localhost:8081 for local development, rather than the dashboard URL.

This application only needs outbound access to the API. The server still needs its configured infrastructure. For direct broker delivery, see HTTP and AMQP workers.

Install the packages

These docs describe SDK 0.5.0. On September 22, 2026, that version was not available from NuGet.org: the existing PipelogiqSDK index ended at 0.4.0-preview.1, and the six split package indexes were absent. Use a local or private feed containing all seven 0.5.0 packages. An older public package is not a substitute for this tutorial.

Prepare a feed from the current source

The following commands check out the public commit verified for these docs and pack the SDK locally. Run them in a directory outside your application. They require the .NET 8 SDK selected by the repository's global.json and internet access for third-party dependencies.

bash
git clone https://github.com/pipelogiq/pipelogiq-sdk-net.git
cd pipelogiq-sdk-net
git checkout --detach 79fdf7ea22f5c972c9460099c8b4e6c2f26c926e
dotnet pack PipelogiqSdk.sln --configuration Release --output ./artifacts/docs-feed

Use the resulting pipelogiq-sdk-net/artifacts/docs-feed directory as the feed path below. It contains all seven SDK .nupkg files. These commands build local artifacts; they do not publish packages or run a Pipelogiq server. The release preparation guide documents the full maintainer verification workflow.

Alternatively, use the exact 0.5.0 package folder supplied by your deployment team.

Create the consumer

Create the application:

bash
dotnet new console --framework net8.0 --name HelloPipelogiq
cd HelloPipelogiq

Create NuGet.Config in this application directory. Replace /absolute/path/to/pipelogiq-packages with the package folder. On Windows, use the corresponding absolute Windows path.

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="pipelogiq" value="/absolute/path/to/pipelogiq-packages" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="pipelogiq">
      <package pattern="PipelogiqSDK*" />
    </packageSource>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

The specific PipelogiqSDK* mapping sends SDK packages to your feed; other dependencies come from NuGet.org. Then install:

bash
dotnet add package PipelogiqSDK.Http --version 0.5.0
dotnet add package Microsoft.Extensions.Hosting --version 8.0.1

PipelogiqSDK.Http includes Core transitively. It does not include RabbitMQ.Client.

Write the application

Replace the entire Program.cs with the following. There are no additional handler or model files to create.

csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PipelogiqSDK.Abstractions;
using PipelogiqSDK.Api;
using PipelogiqSDK.Builders;
using PipelogiqSDK.Configuration;
using PipelogiqSDK.Contracts;
using PipelogiqSDK.Http.Api;
using PipelogiqSDK.Http.Runner;
using PipelogiqSDK.StageHelper;

var apiKey = Environment.GetEnvironmentVariable("PIPELOGIQ_API_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
    throw new InvalidOperationException("Set PIPELOGIQ_API_KEY first.");

var options = new PipelogiqRunnerOptions
{
    ApiKey = apiKey,
    ApiUrl = Environment.GetEnvironmentVariable("PIPELOGIQ_API_URL")
        ?? "http://localhost:8081",
    WorkerName = "hello-http-worker",
    DrainGracePeriod = TimeSpan.FromSeconds(30)
};

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddPipelogiqHttpWorker(options);
builder.Services.AddTransient<HelloHandler>();
using var host = builder.Build();

var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
using var stop = CancellationTokenSource.CreateLinkedTokenSource(
    lifetime.ApplicationStopping);
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(stop.Token);
deadline.CancelAfter(TimeSpan.FromSeconds(90));

var runner = host.Services.GetRequiredService<HttpPipelineRunner>();
runner.RegisterHandler("HelloHandler", typeof(HelloHandler));
await host.StartAsync(stop.Token);
var workerTask = runner.StartAsync(stop.Token);

try
{
    // A new logical run gets a new key. Retrying this builder uses the same key.
    using var pipeline = PipelineBuilder.Create("hello-dotnet", options)
        .WithIdempotencyKey($"hello-{Guid.NewGuid():N}")
        .AddLabel("example", "dotnet-quickstart")
        .WithAction("greet", "HelloHandler", new HelloInput("world"));

    var created = await pipeline.SendAsync(deadline.Token);
    Console.WriteLine($"Pipeline {created.Id} created.");
    using var client = new PipelogiqApiClient(options);

    while (true)
    {
        if (workerTask.IsCompleted)
        {
            await workerTask; // Surface a worker failure before waiting further.
            throw new InvalidOperationException("The worker stopped early.");
        }

        var current = await client.GetPipelineAsync(created.Id, deadline.Token);
        if (current.IsTerminal ?? PipelineStatuses.IsTerminal(current.Status))
        {
            Console.WriteLine($"Pipeline {current.Id}: {current.Status}");
            foreach (var stage in current.Stages ?? new List<StageDto>())
                Console.WriteLine($"{stage.Name}: {stage.Status}{stage.Output}");

            if (!string.Equals(current.Status, PipelineStatuses.Completed,
                    StringComparison.OrdinalIgnoreCase))
                Environment.ExitCode = 1;
            break;
        }

        await Task.Delay(TimeSpan.FromMilliseconds(500), deadline.Token);
    }
}
catch (OperationCanceledException) when (deadline.IsCancellationRequested)
{
    Console.WriteLine("Stopped waiting. The pipeline may still exist on the server.");
    Environment.ExitCode = 1;
}
finally
{
    stop.Cancel();
    try { await workerTask; }
    catch (OperationCanceledException) when (stop.IsCancellationRequested) { }
    finally
    {
        using var shutdown = new CancellationTokenSource(TimeSpan.FromSeconds(40));
        await host.StopAsync(shutdown.Token);
    }
}

internal sealed record HelloInput(string Name);

internal sealed class HelloHandler : IStageHandler<HelloInput>
{
    public Task<IStageResult> ExecuteAsync(
        HelloInput input, IStageContext? context = null)
    {
        context.GetCancellationToken().ThrowIfCancellationRequested();
        var greeting = $"Hello {input.Name}";
        context.AddItem("greeting", greeting);
        context.LogInfo("Greeting prepared.");
        return Task.FromResult<IStageResult>(StageResult.Success(greeting));
    }
}

Run and inspect the result

Set configuration in your terminal; keep the key out of source control:

bash
export PIPELOGIQ_API_URL="http://localhost:8081"
export PIPELOGIQ_API_KEY="your-application-api-key"
dotnet run

PowerShell equivalents:

powershell
$env:PIPELOGIQ_API_URL = "http://localhost:8081"
$env:PIPELOGIQ_API_KEY = "your-application-api-key"
dotnet run

In addition to runtime logs, expect output similar to:

text
Pipeline 123 created.
Pipeline 123: Completed
greet: Completed — Hello world

The numeric ID varies. Open that pipeline in the dashboard to inspect the stage input, output, log, and greeting context value. Submission returns a pipeline ID before the work finishes; the status loop observes completion separately.

The program stops the worker after observing a terminal result. Ctrl+C uses the host's shutdown signal. A timeout only stops this local example from waiting; it does not call the pipeline cancellation API.

If it does not complete

SymptomCheck
NU1101 or missing versionYour configured feed contains both HTTP and Core 0.5.0 packages and their internal dependencies.
Authentication failureUse an application API key and the external API URL.
Pipeline stays unstartedAPI and scheduler are running; HelloHandler is registered under exactly the submitted name.
Worker repeatedly bootstrapsCheck runtime logs, server compatibility, API reachability, and queue provisioning on the server.
Pipeline failsInspect each stage's output, LastErrorCode, attempts, and logs.

Next, add workflow dependencies and retries, or separate this producer and worker into different processes using the worker guide.

Download this page as MarkdownMatches the 0.5.0 source release

Start here