PlanSolve 0.29.0

dotnet add package PlanSolve --version 0.29.0
                    
NuGet\Install-Package PlanSolve -Version 0.29.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PlanSolve" Version="0.29.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PlanSolve" Version="0.29.0" />
                    
Directory.Packages.props
<PackageReference Include="PlanSolve" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PlanSolve --version 0.29.0
                    
#r "nuget: PlanSolve, 0.29.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PlanSolve@0.29.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PlanSolve&version=0.29.0
                    
Install as a Cake Addin
#tool nuget:?package=PlanSolve&version=0.29.0
                    
Install as a Cake Tool

PlanSolve for .NET

Official .NET client for the PlanSolve optimization API. One typed client covers three solvers (field service routing, professional-services task assignment, and shift scheduling) with async/await, built-in polling, and clean error messages.

Installation

dotnet add package PlanSolve

Targets .NET 10.

Quick start

using PlanSolve;
using PlanSolve.FieldService;

using var client = new PlanSolveClient("YOUR_API_KEY");

var request = new FieldServiceStartRequest
{
    Vehicles =
    {
        new Vehicle
        {
            Id = "tech1",
            Location = [40.7128, -74.0060],
            Skills = ["repair"],
            Shifts = [new Shift(DateTime.Parse("2026-04-02T08:00:00"), DateTime.Parse("2026-04-02T17:00:00"))],
        },
    },
    Visits =
    {
        new Visit
        {
            Id = "visit1",
            Name = "AC Repair - Downtown Office",
            Location = [40.7589, -73.9851],
            ServiceDuration = Duration.FromMinutes(60),
            Priority = "HIGH",
            RequiredSkills = ["repair"],
        },
    },
};

// Submit and await the optimized plan in a single call
var result = await client.FieldService.StartAndWaitForCompletionAsync(request);

foreach (var vehicle in result.Vehicles)
    Console.WriteLine($"Vehicle {vehicle.Id}: {vehicle.Visits.Count} visits");

Solvers

One client, three solvers. All share the same submit, poll, result workflow:

Solver Accessor Use for
Field Service client.FieldService Vehicle routing with travel time, time windows, and skills
Professional Services client.ProfessionalServices Task assignment by skill, availability, priority, and deadlines
Shift client.Shift Shift scheduling across contracts, availability, and fairness

Every accessor exposes the same methods:

Method HTTP Returns
StartAsync(request) POST /api/v1/{solver} Start response with the JobId
GetStatusAsync(jobId) GET /api/v1/{solver}/{jobId}/status SolverStatusResponse
GetResultAsync(jobId) GET /api/v1/{solver}/{jobId} The solver's result type
StopAsync(jobId) DELETE /api/v1/{solver}/{jobId} The best solution found so far (same type as GetResultAsync)
GetAnalysisAsync(jobId) GET /api/v1/{solver}/{jobId}/analyze AnalysisResponse (constraint breakdown)
WaitForCompletionAsync(jobId, ...) polls status, then fetches the result The solver's result type
StartAndWaitForCompletionAsync(request, ...) start + wait The solver's result type

GetStatusAsync, GetResultAsync, StopAsync and GetAnalysisAsync also have an overload that takes a CancellationToken.

// Stop a long-running solve early and keep the best plan found so far
var best = await client.FieldService.StopAsync(jobId);

// Explain the score: which constraints were broken, and by what
var analysis = await client.FieldService.GetAnalysisAsync(jobId);

Polling

WaitForCompletionAsync and StartAndWaitForCompletionAsync poll the status endpoint every pollIntervalMs milliseconds, up to maxAttempts times. The defaults are 5000 ms x 150 attempts (12.5 minutes), which covers the server's 10 minute solve cap with headroom. Passing a value of 0 or less uses the default.

A job is complete when the status reports Solving == false and SolverStatus == NOT_SOLVING. The result is then fetched with GetResultAsync. Status polls and result fetches are retried on failure (up to 3 retries with 2, 4 and 8 second backoff) before the error is thrown. StopAsync is not retried. The library never writes to the console.

Configuration

Pass your API key to the constructor: new PlanSolveClient("...") (an overload accepts a custom HttpClient). It is sent as the X-API-KEY header on every request. PlanSolveClient is IDisposable, so wrap it in using.

Error handling

Exception When
HttpRequestException The API returned a non-2xx status. .StatusCode is populated and the message is readable, e.g. Request failed with status code 400 (BadRequest): vehicles: At least one vehicle is required. A failed solve answers the status endpoint with 422, which surfaces here while waiting.
ArgumentException jobId is null or empty (message: jobId cannot be null or empty. Pass the JobId returned by StartAsync., ParamName is jobId).
TimeoutException The solver is still running after maxAttempts polls (message: Solver still running after N polls; raise maxAttempts or lower options.spentLimit.).
OperationCanceledException The CancellationToken you passed was cancelled (may be the TaskCanceledException subtype). Cancellations are never retried.
System.Text.Json.JsonException A response body could not be deserialized. The message includes the JSON path and a truncated copy of the body.
try
{
    var result = await client.FieldService.StartAndWaitForCompletionAsync(request);
}
catch (HttpRequestException e)
{
    Console.WriteLine($"API error {(int?)e.StatusCode}: {e.Message}");
}
catch (TimeoutException e)
{
    Console.WriteLine(e.Message);
}

Documentation

Full guides, per-solver data models, and parameter reference live on the docs site:

Package: NuGet

License

Apache-2.0

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.29.0 90 9/22/2026
0.28.0 89 9/22/2026
0.27.0 126 7/23/2026
0.26.0 115 7/22/2026
0.25.8 122 7/22/2026
0.25.7 112 7/21/2026
0.25.6 111 7/21/2026
0.25.5 117 7/21/2026
0.25.4 116 7/21/2026
0.25.3 109 7/21/2026
0.25.2 114 7/21/2026
0.25.1 106 7/21/2026
0.25.0 113 7/21/2026