TofuPilot 2.12.0
dotnet add package TofuPilot --version 2.12.0
NuGet\Install-Package TofuPilot -Version 2.12.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="TofuPilot" Version="2.12.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TofuPilot" Version="2.12.0" />
<PackageReference Include="TofuPilot" />
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 TofuPilot --version 2.12.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: TofuPilot, 2.12.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 TofuPilot@2.12.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=TofuPilot&version=2.12.0
#tool nuget:?package=TofuPilot&version=2.12.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
TofuPilot C# SDK
The official C# SDK for TofuPilot. Integrate your hardware test runs into one app with just a few lines of C#.
Installation
Add a project reference:
dotnet add reference path/to/TofuPilot.csproj
Quick Start
using TofuPilot;
using TofuPilot.Models.Requests;
var client = new TofuPilot(apiKey: Environment.GetEnvironmentVariable("TOFUPILOT_API_KEY")!);
var run = await client.Runs.CreateAsync(new RunCreateRequest
{
ProcedureId = "your-procedure-id",
SerialNumber = "SN001",
PartNumber = "PN001",
Outcome = RunCreateOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
});
Console.WriteLine($"Run created: {run.Id}");
All async methods support CancellationToken:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var run = await client.Runs.CreateAsync(request, cts.Token);
Authentication
Set your API key as an environment variable:
export TOFUPILOT_API_KEY="your-api-key"
Or pass it directly:
var client = new TofuPilot(apiKey: "your-api-key");
To point to a different server (e.g. self-hosted):
var client = new TofuPilot(
apiKey: "your-api-key",
serverUrl: "https://your-instance.com/api"
);
Custom certificates
using System.Security.Cryptography.X509Certificates;
using TofuPilot.Utils;
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(new X509Certificate2("client.pfx", "password"));
var client = new TofuPilot(
apiKey: "your-api-key",
client: new TofuPilotHttpClient(handler)
);
Available Resources
| Resource | Operations |
|---|---|
client.Runs |
List, Create, Get, Delete, Update |
client.Runs.Attachments() |
UploadAsync, DownloadAsync |
client.Units |
List, Create, Get, Delete, Update, AddChild, RemoveChild |
client.Units.Attachments() |
UploadAsync, DownloadAsync, DeleteAsync |
client.Parts |
List, Create, Get, Delete, Update |
client.Parts.Revisions |
Create, Get, Delete, Update |
client.Procedures |
List, Create, Get, Delete, Update |
client.Procedures.Versions |
Create, Get, Delete |
client.Batches |
List, Create, Get, Delete, Update |
client.Stations |
List, Create, Get, GetCurrent, Remove, Update |
client.User |
List |
Usage Examples
Create a run with measurements
var run = await client.Runs.CreateAsync(new RunCreateRequest
{
ProcedureId = procedureId,
SerialNumber = "SN-001",
PartNumber = "PCB-V1",
Outcome = RunCreateOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
Phases = new List<RunCreatePhases>
{
new()
{
Name = "Voltage Test",
Outcome = RunCreatePhasesOutcome.Pass,
StartedAt = DateTime.UtcNow.AddMinutes(-5),
EndedAt = DateTime.UtcNow,
Measurements = new List<RunCreateMeasurements>
{
new()
{
Name = "Output Voltage",
Outcome = RunCreateMeasurementsOutcome.Pass,
MeasuredValue = 3.3,
Units = RunCreateUnits.CreateStr("V"),
Validators = new List<RunCreateMeasurementsValidators>
{
new() { Operator = ">=", ExpectedValue = RunCreateMeasurementsExpectedValue.CreateNumber(3.0) },
new() { Operator = "<=", ExpectedValue = RunCreateMeasurementsExpectedValue.CreateNumber(3.6) },
},
},
},
},
},
});
List and filter runs
var result = await client.Runs.ListAsync(
partNumbers: new List<string> { "PCB-V1" },
outcomes: new List<RunListQueryParamOutcome> { RunListQueryParamOutcome.Pass },
limit: 10
);
foreach (var run in result.Data)
Console.WriteLine($"{run.Id} — {run.Unit.SerialNumber}");
Manage units and sub-units
// Create part and revision
await client.Parts.CreateAsync(new PartCreateRequest { Number = "PCB-V1", Name = "Main Board" });
await client.Parts.Revisions.CreateAsync("PCB-V1", new PartCreateRevisionRequestBody { Number = "REV-A" });
// Create units
await client.Units.CreateAsync(new UnitCreateRequest
{
SerialNumber = "PARENT-001",
PartNumber = "PCB-V1",
RevisionNumber = "REV-A",
});
await client.Units.CreateAsync(new UnitCreateRequest
{
SerialNumber = "CHILD-001",
PartNumber = "PCB-V1",
RevisionNumber = "REV-A",
});
// Link parent-child
await client.Units.AddChildAsync("PARENT-001", new UnitAddChildRequestBody
{
ChildSerialNumber = "CHILD-001",
});
Attach files to runs and units
// Upload a file to a run
await client.Runs.Attachments().UploadAsync(runId, "report.pdf");
// Upload a file to a unit
await client.Units.Attachments().UploadAsync("SN-0001", "calibration.pdf");
// Download an attachment
var run = await client.Runs.GetAsync(runId);
await client.Runs.Attachments().DownloadAsync(run.Attachments[0].DownloadUrl, "local-copy.pdf");
// Delete a unit attachment
await client.Units.Attachments().DeleteAsync("SN-0001", new List<string> { attachmentId });
Error Handling
API errors throw typed exceptions:
using TofuPilot.Models.Errors;
try
{
await client.Runs.GetAsync("nonexistent-id");
}
catch (NotFoundException ex)
{
Console.WriteLine($"Not found: {ex.Message}");
}
catch (BadRequestException ex)
{
Console.WriteLine($"Bad request: {ex.Message}");
}
catch (ApiException ex)
{
Console.WriteLine($"API error {ex.StatusCode}: {ex.Body}");
}
| Exception | Status Code |
|---|---|
BadRequestException |
400 |
UnauthorizedException |
401 |
ForbiddenException |
403 |
NotFoundException |
404 |
ConflictException |
409 |
UnprocessableContentException |
422 |
InternalServerErrorException |
500 |
ApiException |
Any other |
Running Tests
cd clients/csharp/tests
# Create .env.local with your API key and URL:
# TOFUPILOT_URL=http://localhost:3000
# TOFUPILOT_API_KEY_USER=your-api-key
dotnet test
Documentation
Acknowledgments
This package builds on the original C# SDK created by @Hylaean (versions 1.x).
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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.
-
net8.0
- No dependencies.
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 |
|---|---|---|
| 2.12.0 | 47 | 6/6/2026 |
| 2.11.4 | 103 | 5/29/2026 |
| 2.11.3 | 96 | 5/27/2026 |
| 2.7.1 | 101 | 5/10/2026 |
| 2.6.0 | 89 | 5/5/2026 |
| 2.3.4 | 106 | 4/16/2026 |
| 2.3.3 | 106 | 4/15/2026 |
| 2.3.2 | 100 | 4/6/2026 |
| 2.3.1 | 101 | 4/2/2026 |
| 2.3.0 | 106 | 4/1/2026 |
| 2.2.0 | 104 | 4/1/2026 |
| 2.0.2 | 97 | 3/31/2026 |
| 2.0.1 | 101 | 3/31/2026 |
| 1.6.3 | 309 | 2/18/2026 |
| 1.6.2 | 269 | 2/13/2026 |
| 1.6.1 | 123 | 2/13/2026 |
| 1.6.0 | 98 | 2/11/2026 |
| 1.6.0-beta.7 | 58 | 2/11/2026 |
| 1.6.0-beta.3 | 57 | 2/11/2026 |
| 1.5.0 | 178 | 2/10/2026 |
Loading failed