Satya.Sdk 1.0.1

dotnet add package Satya.Sdk --version 1.0.1
                    
NuGet\Install-Package Satya.Sdk -Version 1.0.1
                    
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="Satya.Sdk" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Satya.Sdk" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Satya.Sdk" />
                    
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 Satya.Sdk --version 1.0.1
                    
#r "nuget: Satya.Sdk, 1.0.1"
                    
#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 Satya.Sdk@1.0.1
                    
#: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=Satya.Sdk&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Satya.Sdk&version=1.0.1
                    
Install as a Cake Tool

Satya.Sdk

Official .NET SDK for the Satya API.

This SDK provides a strongly-typed, production-ready client for interacting with Satya services, including authentication, vetting, and reports.


Installation

Install the SDK via NuGet:

dotnet add package Satya.Sdk

Authentication Model (Machine-to-Machine)

This SDK uses a machine-to-machine (M2M) authentication flow.

Each customer owns:

  • Project ID – configured once when creating the client, can be found in Satya application under /settings/account-profile
  • Access Key – stored securely by the customer

The SDK exchanges the access key for a short-lived JWT and automatically manages re-login when needed.

Important Rules

  • You must explicitly log in once before calling protected endpoints
  • Calling a protected endpoint before login results in a local 401 Unauthorized
  • No HTTP request is sent when the JWT is missing
  • After login, the SDK automatically re-logins when the JWT is near expiration or expired
  • The access key is stored in-memory only and is never persisted by the SDK

Quick Start

Create the client:

using Satya.Sdk;
using Satya.Sdk.Vet;

var client = new SatyaClient(new SatyaClientOptions
{
    ProjectId = "your-project-id"
});

Perform mandatory login:

await client.Auth.LoginAndSetTokenAsync("YOUR_ACCESS_KEY");

Call a protected endpoint:

var vettingItem = new VetRequestDto
{
	Name = "John Doe",
	CountryIsoCode = "US",
	ImageBase64 = "base64-encoded-image"
};

var vet = await client.Vet.VetAsync(vettingItem);

API Versioning

  • The SDK builds URLs as "{BaseUrl}/api/{ApiVersion}/..."; default ApiVersion is v1 (backward compatible with unversioned routes on the server).
  • An optional EnableCompatibilityCheck (default false) calls /api/meta once to verify the server’s major version matches the SDK’s expected major. Enable it to enforce compatibility; disable if your server does not expose /api/meta.
  • The server meta endpoint supports both /api/meta and /api/v1/meta; property names are stable (service, apiMajor, apiMinor, apiPatch, supportedMajors).

Authentication Flow

  1. The user calls:

    client.Auth.LoginAndSetTokenAsync(accessKey);
    
  2. The SDK calls the M2M login endpoint and stores:

    • JWT value
    • JWT expiration time (UTC)
    • Access key (in memory)
  3. For every protected request:

    • The JWT is attached as Authorization: Bearer <token>
    • If the JWT is close to expiration, the SDK re-logins automatically
    • If the API responds with code = jwt_expired, the SDK re-logins and retries once

Automatic Re-Login Rules

The SDK WILL re-login automatically when:

  • The JWT expiration time is approaching (proactive refresh)

  • The API responds with:

    { "code": "jwt_expired", "message": "Token is expired or near expiration, please re-login or refresh" }
    

The SDK WILL NOT re-login automatically when:

  • The JWT is missing
  • The JWT is invalid
  • The authorization scheme is incorrect

In these cases, the error is surfaced directly to the caller.


Error Handling

The SDK is designed to throw exceptions and let the consuming application decide how to handle them
(retry, log, show user feedback, persist state, etc.).


Exception taxonomy (what to expect)

SDK consumers should handle errors according to the following rules.


1. ArgumentException — local validation failures (no HTTP call)

Thrown before any request is sent when the payload is invalid:

  • Missing/empty Name
  • Missing/empty ImageBase64
  • Invalid CountryIsoCode format (does not match 2–3 uppercase letters with optional “-” + 2–3 uppercase letters)
  • Null request object

2. ApiException — all Satya SDK / API failures

ApiException is the primary exception type thrown by the Satya SDK.

It is declared in the Satya.Sdk.Internal namespace, so add using Satya.Sdk.Internal; in files that catch it.

It represents all expected failures that can occur while using the SDK, including:

  • API errors (4xx / 5xx)
  • Authentication issues (missing or expired credentials)
  • HTTP request timeouts
  • Automatic re-login failures
  • Vetting wait timeouts (when a vet remains Pending past the wait limit)

Key properties

Each ApiException exposes the following information:

  • StatusCode
    HTTP status code (or logical equivalent for SDK-level errors)

  • Message
    High-level, human-readable description of the failure

  • ResponseBody
    Raw API response body (when available), useful for diagnostics and logging


Example handling
catch (ApiException ex)
{
    Console.WriteLine($"Satya error: {(int)ex.StatusCode} {ex.StatusCode}");
    Console.WriteLine(ex.Message);

    if (!string.IsNullOrWhiteSpace(ex.ResponseBody))
    {
        Console.WriteLine("API response:");
        Console.WriteLine(ex.ResponseBody);
    }

    // Either rethrow, or map to your application's domain-specific error model
    throw;
}
Common ApiException.StatusCode values
Status Code Meaning Recommended Handling
400 BadRequest Request validation failed, or the tenant query limit was reached Correct the payload; for quota, wait for the reset or contact your account team
401 Unauthorized Not logged in / invalid access key Re-run login or fail fast
404 NotFound No vetting record for the supplied PersonId in the active tenant Verify the PersonId returned by the vet request
408 RequestTimeout HTTP request timed out, or a vet stayed Pending past the wait timeout Retry later or increase the timeout
415 UnsupportedMediaType Request was not sent as application/json Should not occur via the SDK; report it if it does
5xx Server error Treat as transient

3. Vetting wait timeout (RequestTimeout)

When using:

  • VetAndWaitAsync
  • WaitForFinalStatusAsync

If the vetting process remains Pending past the configured wait timeout, the SDK throws:

  • ApiException
  • StatusCode = RequestTimeout (408)

This does NOT mean failure — only that vetting is still in progress.

catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.RequestTimeout)
{
    // Vetting is still pending.
    // Persist PersonId and retry later or continue polling.
}

4. Network-level failures

Low-level network issues (DNS, TLS, connection refused) may surface as:

  • HttpRequestException

These are typically transient.

catch (HttpRequestException ex)
{
    // Network failure (DNS, TLS, connection refused).
    // Retry or fail depending on your application logic.
    throw;
}

5. Unexpected exceptions

Any other exception indicates:

  • a bug
  • invalid environment
  • unexpected runtime failure

These should generally be logged and escalated.

catch (Exception ex)
{
    // Unexpected failure
    // Log with stack trace and escalate
    throw;
}

try
{
    await client.Auth.LoginAndSetTokenAsync(accessKey);

    var vetResult = await client.Vet.VetAndWaitAsync(request);

    // Handle final result (Cleared / Suspected)
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.RequestTimeout)
{
    // Vetting still pending – retry later or continue polling
}
catch (ApiException ex)
{
    // Satya API / SDK failure
    throw;
}
catch (HttpRequestException)
{
    // Network failure
    throw;
}
catch (Exception)
{
    // Unexpected failure
    // Log with stack trace and escalate
    throw;
}

Key principles

  • The SDK never swallows errors
  • All expected failures surface as ApiException
  • Retry logic belongs in the consuming application
  • The SDK provides maximum diagnostic information, not policy

Logout

To clear authentication state:

client.Auth.Logout();

After logout, protected calls will again fail with a local 401 until login is performed.


Configuration

SatyaClientOptions controls how the SDK communicates with the Satya API. These options are provided once during client creation.

Available options

  • ProjectId (required)
    Customer-specific project identifier.
    This value uniquely identifies your project and can be found in the Satya application under:
    /settings/account-profile

  • BaseUrl (optional)
    Base URL of the Satya API.
    The SDK is preconfigured with the correct production URL by default.
    This value should NOT be changed unless you were explicitly instructed to do so by the Satya team
    (for example, when using a private deployment, dedicated region, or internal testing environment).

  • Timeout (optional)
    Default timeout applied to all HTTP requests made by the SDK.
    The default value is 3 minutes, which is suitable for long-running operations such as vetting and report generation.
    You may override this value if your application requires a shorter or longer timeout.

  • ApiVersion (optional)
    API version suffix used when building routes (default: v1).
    URLs are composed as {BaseUrl}/api/{ApiVersion}/.... Keep the default unless you are explicitly asked to target a different API version.

  • EnableCompatibilityCheck (optional)
    When true, the SDK calls /api/meta once to verify the server major version matches ApiVersion.
    Default is false. Enable to enforce compatibility; disable if your server does not expose /api/meta.

Use this configuration in almost all production scenarios.

var options = new SatyaClientOptions
{
	ProjectId = "your-project-id"
};

using var client = new SatyaClient(options);

Advanced configuration (only when needed)

Override defaults only when required by your environment or application constraints.

var options = new SatyaClientOptions
{
    ProjectId = "your-project-id",

    // Change BaseUrl only if explicitly instructed by Satya
    BaseUrl = new Uri("https://api.your-domain.com"),

    // Override the default 3-minute timeout if needed
    Timeout = TimeSpan.FromMinutes(5)
};

using var client = new SatyaClient(options);

Thread Safety

  • The SDK is safe for concurrent use
  • Only one re-login operation is performed even if multiple requests detect expiration simultaneously

Versioning

This SDK follows Semantic Versioning:

  • Patch (x.y.Z) – bug fixes
  • Minor (x.Y.z) – backward-compatible features
  • Major (X.y.z) – breaking changes

Requirements

  • .NET 8 or newer (the package targets net8.0)
  • Valid access key and project ID issued by the customer.

Support

For questions, issues, or feature requests:

Usage

This section explains how to use the SDK end-to-end, from submitting a vet request to downloading the final report to a local file.

You can choose between:

  • VetAndWaitAsync – submit and wait in a single call (recommended)
  • VetAsync + WaitForFinalStatusAsync – submit first, then wait explicitly

Default waiting behavior:

  • Poll interval: 5 minutes
  • Timeout: 24 hours

1. Install and import

Install the SDK via NuGet and import the required namespaces.

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

using Satya.Sdk;          // SatyaClient, SatyaClientOptions
using Satya.Sdk.Vet;      // VetRequestDto, VetResponseDto, VetStatus
using Satya.Sdk.Internal; // ApiException

2. Create the SDK client

Create the client once and reuse it for all vetting operations.

var options = new SatyaClientOptions
{
    ProjectId = "your-project-id"
};

using var client = new SatyaClient(options);

3. Login

Perform mandatory login.

await client.Auth.LoginAndSetTokenAsync("YOUR_ACCESS_KEY");

4. Build the vet request

Prepare the vet request with the subject’s identifying information.

var vettingItem = new VetRequestDto
{
    Name = "John Doe",
    CountryIsoCode = "US",
    ImageBase64 = "base64-encoded-image"
};

Input requirements (VetRequestDto):

  • Name (required): must not be empty.
  • CountryIsoCode (optional): 2–3 uppercase letters, optional dash + 2–3 uppercase letters for region (e.g. US, USA, US-NY, USA-NY, US-NYC, USA-NYC).
  • ImageBase64 (required): valid Base64-encoded image string (raw bytes; no data URI prefix).

Use VetAndWaitAsync when you want the SDK to handle polling internally and return only a final result.

  • Blocks until the vet reaches a final state
  • Final states are typically Cleared or Suspected
  • Uses default polling (every 5 minutes, up to 24 hours)
  • Throws on failure or timeout
VetResponseDto vetResult = await client.Vet.VetAndWaitAsync(
    vettingItem
);

Console.WriteLine($"Final status: {vetResult.VetStatus}");

Option B: Submit first, then wait explicitly

Use this approach when you need the PersonId immediately (for persistence, correlation, or later reuse).

5. Submit the vet request

VetResponseDto vetResult = await client.Vet.VetAsync(
    vettingItem
);

Console.WriteLine(
    $"Submitted vet. PersonId={vetResult.PersonId}, Status={vetResult.VetStatus}"
);

6. Wait for the final status

WaitForFinalStatusAsync:

  • Returns immediately if the status is already final
  • Polls automatically if the status is Pending
  • Uses default polling (every 5 minutes, up to 24 hours)
if (vetResult.VetStatus is VetStatus.Pending)
{
    vetResult = await client.Vet.WaitForFinalStatusAsync(
        vetResult.PersonId
    );
}

Console.WriteLine($"Final status: {vetResult.VetStatus}");

7. Handle the final status

After either waiting approach, the vet result is guaranteed to be final.

switch (vetResult.VetStatus)
{
    case VetStatus.Cleared:
        // Handle Cleared status scenario
        Console.WriteLine("Vetting completed: CLEARED");
        break;

    case VetStatus.Suspected:
        // Handle Suspected status scenario
        Console.WriteLine("Vetting completed: SUSPECTED");
        break;
    default:
        throw new InvalidOperationException(
            $"Unexpected final status: {vetResult.VetStatus}");
}

Output (VetResponseDto):

  • PersonId: unique identifier of the vetted person; use to poll or correlate.
  • VetStatus: Cleared, Pending, Suspected, or NotVetted. Only Cleared and Suspected are treated as final by the wait helpers, so Pending should not appear after a wait.
  • ReportUrl: single pre-signed report link, valid for 5 minutes; download within that window. It points to the in-depth report when Suspected and the essential report when Cleared. It is empty while Pending, and also when your project is not permitted to receive reports.
  • Vetting: list of findings/decisions (populated when Suspected; empty for Cleared).

8. Download the report to a local file

Once the vet reaches a final status (Cleared or Suspected), download the report using the provided report URL.

// Download the report to a local file
if (!string.IsNullOrWhiteSpace(vetResult.ReportUrl))
{
    string directory = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
        "Satya",
        "Reports"
    );

    string fileName =
        $"{vettingItem.Name}_" +
        $"{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss_fff}.pdf";

    string fullPath = Path.Combine(directory, fileName);

    await client.Report.DownloadReportToFileAsync(vetResult.ReportUrl, fullPath);

    Console.WriteLine($"Report saved to: {fullPath}");
}

ReportUrl is signed for 5 minutes, so download it as soon as the vet reaches a final status rather than persisting the URL for later use.


Submit → wait → validate result → download report.

public static async Task RunAsync()
{
    var options = new SatyaClientOptions
	{
		ProjectId = "your-project-id"
	};

	using var client = new SatyaClient(options);

	await client.Auth.LoginAndSetTokenAsync("YOUR_ACCESS_KEY");

	var vettingItem = new VetRequestDto
	{
		Name = "John Doe",
		CountryIsoCode = "US",
		ImageBase64 = "base64-encoded-image"
	};

	#region Option A

	VetResponseDto vetResult = await client.Vet.VetAndWaitAsync(
		vettingItem
	);

	#endregion

	#region Option B

	//VetResponseDto vetResult = await client.Vet.VetAsync(
	//    vettingItem
	//);

	//if (vetResult.VetStatus is VetStatus.Pending)
	//{
	//    vetResult = await client.Vet.WaitForFinalStatusAsync(
	//        vetResult.PersonId
	//    );
	//}

	#endregion

	switch (vetResult.VetStatus)
	{
		case VetStatus.Cleared:
			// Handle Cleared status scenario
			Console.WriteLine("Vetting completed: CLEARED");
			break;

		case VetStatus.Suspected:
			// Handle Suspected status scenario
			Console.WriteLine("Vetting completed: SUSPECTED");
			break;
		default:
			throw new InvalidOperationException(
				$"Unexpected final status: {vetResult.VetStatus}");
	}

	// Download the report to a local file
	if (!string.IsNullOrWhiteSpace(vetResult.ReportUrl))
	{
		string directory = Path.Combine(
			Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
			"Satya",
			"Reports"
		);

		string fileName =
			$"{vettingItem.Name}_" +
			$"{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss_fff}.pdf";

		string fullPath = Path.Combine(directory, fileName);

		await client.Report.DownloadReportToFileAsync(vetResult.ReportUrl, fullPath);

		Console.WriteLine($"Report saved to: {fullPath}");
	}
}

Summary

  • Prefer VetAndWaitAsync for the simplest integration
  • Use VetAsync + WaitForFinalStatusAsync when you need lifecycle control
  • Always handle final statuses explicitly
  • Reports are downloaded via Report.DownloadReportToFileAsync
Product 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
1.0.1 101 9/7/2026
1.0.0 158 1/11/2026

Documentation-only release; no API or behavior changes. Corrected README samples to use VetStatus and ReportUrl, added the required using directives, replaced the nonexistent 429 entry in the ApiException status code table with 400/404/415, and raised the stated minimum from .NET 6 to .NET 8. VetAndWaitAsync and WaitForFinalStatusAsync XML docs now correctly document ApiException with StatusCode RequestTimeout (408) instead of TimeoutException.