Verifiabl 0.2.0
Prefix ReservedRenamed to Verifiabl.Issuer
See the version list below for details.
dotnet add package Verifiabl --version 0.2.0
NuGet\Install-Package Verifiabl -Version 0.2.0
<PackageReference Include="Verifiabl" Version="0.2.0" />
<PackageVersion Include="Verifiabl" Version="0.2.0" />
<PackageReference Include="Verifiabl" />
paket add Verifiabl --version 0.2.0
#r "nuget: Verifiabl, 0.2.0"
#:package Verifiabl@0.2.0
#addin nuget:?package=Verifiabl&version=0.2.0
#tool nuget:?package=Verifiabl&version=0.2.0
Verifiabl .NET SDK
Official .NET SDK for issuing Verifiabl payslip QR codes.
Add a scannable QR code to each payslip you issue. You register the non-PII payslip data with Verifiabl and encrypt the employee's personal details on your own infrastructure, so they live only inside the QR code on the document and never reach Verifiabl.
Verifiabl is for accredited payroll providers. You receive sandbox credentials at onboarding. Full documentation is at docs.verifiabl.io.
Installation
dotnet add package Verifiabl
In an app that uses dependency injection, add the integration package too:
dotnet add package Verifiabl.Extensions.DependencyInjection
Supported targets: .NET 8+, and .NET Framework 4.7.2+ (Windows). On .NET Framework, AES-GCM is provided by the bundled Microsoft.Bcl.Cryptography dependency.
Two namespaces: offline and networked
The split is deliberate, so you can see at a glance which half of the SDK touches the network.
| Namespace | Contents | Network |
|---|---|---|
Verifiabl |
Pii, PiiFields, VerifiablCrypto, EncryptedPii, EncryptionMetadata, VerifiablBarcode, BarcodeParts, BarcodeSvgOptions, VerifiablReference, VerifiablEnvironment, VerifiablEndpoints |
None. Pure functions you can call from anywhere, including a hot PDF-rendering loop. |
Verifiabl.Client |
IVerifiablClient, VerifiablClient, VerifiablClientOptions, VerifiablAuth, the request/response types, VerifiablApiException and friends |
Calls the Verifiabl issuer API. |
Encryption, PII formatting, reference generation, and barcode rendering all happen on your infrastructure with no network call — the Verifiabl namespace has no client in it to make one.
Registering the client
With dependency injection, using Verifiabl.Extensions.DependencyInjection:
using Microsoft.Extensions.DependencyInjection;
using Verifiabl;
using Verifiabl.Client;
builder.Services.AddVerifiablClient(options =>
{
options.Environment = VerifiablEnvironment.Sandbox;
options.Auth = VerifiablAuth.ClientCredentials(
builder.Configuration["Verifiabl:ClientId"]!,
builder.Configuration["Verifiabl:ClientSecret"]!);
});
IVerifiablClient is registered as a singleton — it caches OAuth access tokens, so a shorter lifetime would fetch a new token on every resolve — and its HttpClient comes from IHttpClientFactory. Inject IVerifiablClient wherever you need it; substitute your own implementation in tests.
Without dependency injection, construct it once and reuse it (it is thread-safe):
var client = new VerifiablClient(new VerifiablClientOptions
{
Environment = VerifiablEnvironment.Sandbox,
Auth = VerifiablAuth.ClientCredentials(clientId, clientSecret),
});
Getting started
This is the self-managed flow: register the payslip, encrypt the personal details locally, and generate the QR code yourself. You need four values from onboarding: your OAuth client ID and secret, your encryption key, and your key version.
using Verifiabl;
using Verifiabl.Client;
// Your 32-byte key and key version, from onboarding. Load the key from a secrets manager.
byte[] key = Convert.FromBase64String(
Environment.GetEnvironmentVariable("VERIFIABL_ENCRYPTION_KEY_BASE64")!);
string keyVersion = Environment.GetEnvironmentVariable("VERIFIABL_KEY_VERSION")!; // e.g. "0f8fad5b-...e.1"
// 1. Format and encrypt the employee's details locally.
string pii = Pii.Format(new PiiFields
{
EmployeeName = "Jane A. Doe",
Position = "Senior Developer",
Department = "Engineering",
EmployerAbn = "12345678901",
Bsb = "062-000",
AccountNumber = "12345678",
AccountName = "Jane A Doe",
});
EncryptedPii encrypted = VerifiablCrypto.EncryptPii(pii, key, keyVersion);
// 2. Register the non-PII data. Verifiabl returns a Verifiabl reference.
RegisterNonPiiResponse registration = await client.RegisterNonPiiAsync(new RegisterNonPiiRequest
{
Schema = "au.payslip.v1",
IssuedAt = DateTimeOffset.UtcNow,
PayslipNonPii = new PayslipNonPii
{
PeriodStart = "2026-05-01",
PeriodEnd = "2026-05-31",
// au.payslip.v1 requires these; keys and value types are set by the schema.
AdditionalData = new Dictionary<string, object?>
{
["payment_date"] = "2026-06-04",
["currency"] = "AUD",
["gross_cents"] = 812_500,
["paygw_cents"] = 203_000,
["net_cents"] = 609_500,
["ytd_gross_cents"] = 8_937_500,
["ytd_paygw_cents"] = 2_233_000,
},
},
EncryptionMetadata = encrypted.Metadata,
});
// 3. Render the QR code and embed the SVG in your payslip PDF.
BarcodeSvgResult badge = VerifiablBarcode.CreateSvg(
new BarcodeParts(registration.VerifiablReference, encrypted.Ciphertext),
new BarcodeSvgOptions { Environment = VerifiablEnvironment.Sandbox });
The compiler enforces the mandatory fields: Schema, IssuedAt, PayslipNonPii, and EncryptionMetadata are required, so an incomplete request will not build.
AdditionalData is passed to the API verbatim under the exact keys you supply. Values may be strings, booleans, numbers, null, nested dictionaries, or sequences of those; anything else throws an ArgumentException naming the key. Which keys your schema requires is documented per schema — the au.payslip.v1 set is shown above.
VerifiablBarcode.CreateSvg produces a standalone SVG that scales to any size without losing quality; embed it directly in your PDF pipeline. If you need a raster image instead, let the API build a PNG for you with client.RegisterAndBuildBarcodeAsync, or rasterise the SVG with your own renderer. See the docs for both flows.
Retries and idempotency
Failed requests are retried automatically with exponential backoff (VerifiablClientOptions.MaxRetries, default 2). The Verifiabl reference is the idempotency key, so retries are only applied where they are safe. RegisterNonPiiAsync generates a reference client-side (or uses the one you set on the request), so the API deduplicates a re-send and the SDK retries it on throttling, timeouts, 5xx, and network faults — same as batch registration. RegisterAndBuildBarcodeAsync lets the API assign the reference and cannot be deduplicated, so it retries only 429, which is enforced before any processing.
Batch registration
For pay runs, register up to 1000 records in one request with RegisterNonPiiBatchAsync. The provider generates each Verifiabl reference up-front with VerifiablReference.Generate() and includes it on each record, so the whole batch can go in one round trip. Results are returned index-aligned to the input; one bad record never fails the whole batch.
DateTimeOffset issuedAt = DateTimeOffset.UtcNow;
var prepared = payslips.Select(payslip =>
{
string verifiablReference = VerifiablReference.Generate();
EncryptedPii encrypted = VerifiablCrypto.EncryptPii(Pii.Format(payslip.Pii), key, keyVersion);
// Keep the ciphertext alongside the reference locally: you need both to render the barcode.
return (verifiablReference, encrypted, payslip);
}).ToList();
RegisterNonPiiBatchResponse batch = await client.RegisterNonPiiBatchAsync(
prepared.Select(item => new BatchRecord
{
VerifiablReference = item.verifiablReference,
Schema = "au.payslip.v1",
IssuedAt = issuedAt,
PayslipNonPii = new PayslipNonPii
{
PeriodStart = item.payslip.PeriodStart,
PeriodEnd = item.payslip.PeriodEnd,
AdditionalData = item.payslip.SchemaFields,
},
EncryptionMetadata = item.encrypted.Metadata,
}));
foreach (BatchRecordResult result in batch.Results)
{
if (result.Status == BatchRecordStatuses.Error)
{
logger.LogError(
"Record {Reference} failed: {Code} {Detail}",
result.VerifiablReference, result.Code, result.Detail);
}
}
Environments
Set Environment to VerifiablEnvironment.Production (the default) or VerifiablEnvironment.Sandbox. Pass the same value to the client and the barcode renderer, so the scan URL printed on the document matches where the record was registered.
Errors
Every failure an API call reports derives from VerifiablException, so one catch clause covers the client. Match the specific types when you want to react differently.
try
{
await client.RegisterNonPiiAsync(request);
}
catch (VerifiablApiException exception) when (exception.Code == VerifiablErrorCodes.ValidationFailed)
{
logger.LogWarning("Validation failed, request id {RequestId}", exception.RequestId);
}
catch (VerifiablException exception)
{
// VerifiablApiException, VerifiablAuthException, VerifiablTimeoutException,
// and VerifiablTransportException all land here.
logger.LogError(exception, "Verifiabl registration failed");
}
| Exception | Raised when |
|---|---|
VerifiablApiException |
The API returned a non-2xx response. Carries Status, a stable Code, the parsed Body, and a RequestId to quote to support. |
VerifiablAuthException |
An OAuth access token could not be obtained. |
VerifiablTimeoutException |
The call exceeded VerifiablClientOptions.Timeout, which covers the token fetch, the request, and every retry. |
VerifiablTransportException |
A network fault prevented a response (the HttpRequestException is the InnerException), or a 2xx response was not usable JSON. |
Two things are deliberately not VerifiablException: an ArgumentException for an incomplete or malformed request, thrown before anything is sent, and an OperationCanceledException when you cancel the CancellationToken you passed in.
Security
Employee PII is encrypted on your infrastructure and never reaches Verifiabl. Keep your encryption key and OAuth secret in a secrets manager. See the security model for the full detail.
Documentation
Full API reference, the alternative API flow, barcode placement rules, and the security model are at docs.verifiabl.io.
License
| 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 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. |
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- Microsoft.Bcl.Cryptography (>= 10.0.5)
- Net.Codecrete.QrCodeGenerator (>= 3.0.0)
- System.Text.Json (>= 10.0.5)
-
net10.0
- Net.Codecrete.QrCodeGenerator (>= 3.0.0)
-
net8.0
- Net.Codecrete.QrCodeGenerator (>= 3.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Verifiabl:
| Package | Downloads |
|---|---|
|
Verifiabl.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection integration for the Verifiabl .NET SDK: registers IVerifiablClient as a singleton wired to IHttpClientFactory. |
GitHub repositories
This package is not used by any popular GitHub repositories.