Configuration & Dependency Injection

Learn how to configure the .NET Custody SDK with dependency injection, environment settings, and authentication options.

SDK-Only Access

To ensure your integration remains stable and supported, always use the official SDKs. The underlying API endpoints are internal implementation details that may change in purpose, functionality, or be removed at any time without notice. Direct API access is not supported. Learn more →

The SDK handles environment routing automatically — use the provided environment methods rather than targeting endpoints directly.

Basic Configuration

Register the Custody Client in your ASP.NET Core application's Program.cs:

using CoxAuto.Vecu.CustodySdk.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Register Custody Client with token provider
builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Sandbox;
    options.TokenProvider = async (cancellationToken) =>
    {
        var token = await myAuthClient.GetTokenAsync(cancellationToken);
        return token.AccessToken;
    };
});

var app = builder.Build();

Configuration Options

The SDK provides comprehensive configuration through CustodyClientOptions:

EnvironmentCustodyEnvironmentrequired

Target environment (Sandbox, NonProd, PreProd, or Production). Defaults to Sandbox.

TokenProviderFunc<CancellationToken, Task<string>>required

Callback that the SDK invokes to obtain a bearer token. Required unless UseMockData is true. The SDK exchanges this token for an internal service token automatically.

InitialTokenstring

Optional initial bearer token to use before invoking TokenProvider. Useful when a token is already available at startup to avoid the first callback.

TimeoutSecondsint

Request timeout in seconds (1-300). Defaults to 30 seconds.

MaxRetriesint

Maximum retry attempts for failed requests (0-10). Defaults to 3 retries with exponential backoff.

BaseUrlOverridestring?

For local mock server testing only. The SDK automatically routes to the correct environment URL. Do not use this to target API endpoints directly.

EnableDetailedLoggingbool

Enable detailed request/response logging. Not recommended for production due to performance impact.

UseMockDatabool

Enable mock mode for testing without API calls. Returns realistic mock data. Defaults to false.

Environment Configuration

Available Environments

The SDK supports four environments, all using the same authentication model:

EnvironmentAuthenticationUse Case
SandboxToken ProviderDevelopment & testing
NonProdToken ProviderIntegration testing
PreProdToken ProviderPre-production validation
ProductionToken ProviderLive production

Using Environment Presets

Configure for specific environments using the Environment property. All environments use the same TokenProvider callback:

// Sandbox
builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Sandbox;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
});

// NonProd
builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.NonProd;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
});

// Production
builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Production;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
});

Authentication

Token Provider Callback

The SDK uses a single authentication model across all environments. You provide a TokenProvider callback that returns a bearer token (e.g., a CIP token from your auth system). The SDK then exchanges it internally for a VECU service token.

builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Production;
    options.TokenProvider = async (cancellationToken) =>
    {
        // Return a bearer token from your auth system (e.g., CIP token)
        var token = await myAuthClient.GetTokenAsync(cancellationToken);
        return token.AccessToken;
    };
});

The CancellationToken parameter allows the SDK to cancel the token request if the overall operation is cancelled. Always pass it through to your async auth calls.

Initial Token (Optional)

If you already have a valid bearer token at startup (e.g., from a previous session), provide it as InitialToken to skip the first TokenProvider callback:

builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Production;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
    options.InitialToken = cachedBearerToken; // Used until first refresh
});

When TokenProvider Is Called

The SDK calls your TokenProvider callback in these scenarios:

  • First request — unless InitialToken is provided
  • Token refresh — when the cached VECU token expires (refreshed automatically 5 minutes before expiry)
  • Stale client token — if the token exchange endpoint returns 401 or 410, the SDK clears the cached client token, calls TokenProvider again
  • API token rejection — if the Custody API returns 401, the SDK invalidates the cached VECU token and re-acquires from step 1

The VECU token is cached as a singleton — it is shared across all requests to your application. Your TokenProvider is only called when a new client token is actually needed, not on every request.

TokenProvider is required unless UseMockData = true. If neither is configured, the SDK throws a validation error on startup.

Configuration from appsettings.json

Bind configuration from appsettings.json for better separation of concerns. Note that TokenProvider is a code callback and cannot be represented in JSON — it must be set in code:

{
  "CustodyClient": {
    "Environment": "Sandbox",
    "TimeoutSeconds": 30,
    "MaxRetries": 3,
    "EnableDetailedLogging": false
  }
}

Register with configuration binding and token provider:

builder.Services.AddCustodyClient(
    builder.Configuration,
    tokenProvider: async (cancellationToken) =>
    {
        var token = await myAuthClient.GetTokenAsync(cancellationToken);
        return token.AccessToken;
    });

Environment-Specific Configuration

Use different appsettings.{Environment}.json files for each environment:

appsettings.Development.json

{
  "CustodyClient": {
    "Environment": "Sandbox",
    "EnableDetailedLogging": true
  }
}

appsettings.Production.json

{
  "CustodyClient": {
    "Environment": "Production",
    "TimeoutSeconds": 60,
    "MaxRetries": 5,
    "EnableDetailedLogging": false
  }
}

Store any credentials used by your TokenProvider callback (client IDs, secrets, certificates) in Azure Key Vault, AWS Secrets Manager, or environment variables rather than committing them to source control.

Timeout & Retry Configuration

Configure resilience settings for production workloads:

builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Production;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);

    // Increase timeout for long-running operations
    options.TimeoutSeconds = 60; // 1 minute

    // More aggressive retry policy
    options.MaxRetries = 5; // Retry up to 5 times
});

The SDK uses exponential backoff for retries with the following pattern:

  • 1st retry: 2 seconds
  • 2nd retry: 4 seconds
  • 3rd retry: 8 seconds
  • 4th retry: 16 seconds
  • 5th retry: 32 seconds

Retries are only attempted for transient failures (network errors, 5xx server errors, rate limiting). Client errors (4xx) are not retried.

Advanced Configuration

Local Mock Server Testing

Override the base URL when testing against a local mock server:

builder.Services.AddCustodyClient(options =>
{
    options.BaseUrlOverride = "http://localhost:8080";
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
});

Detailed Logging for Debugging

Enable detailed HTTP request/response logging:

builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Sandbox;
    options.TokenProvider = async (ct) => await myAuthClient.GetTokenAsync(ct);
    options.EnableDetailedLogging = true; // Log all requests/responses
});

Detailed logging can impact performance and may log sensitive data. Only enable in development environments.

Mock Mode for Testing

Enable mock mode to return realistic test data without making API calls:

builder.Services.AddCustodyClient(options =>
{
    options.Environment = CustodyEnvironment.Sandbox;
    options.UseMockData = true; // No real API calls, no TokenProvider needed
});

When mock mode is enabled:

  • No authentication required
  • No network calls made
  • Deterministic mock data returned
  • In-memory state management for authorizations

See Testing for detailed mock mode usage.

Using the Client

Once registered, inject ICustodyServiceClient into your services:

public class VehicleCustodyService
{
    private readonly ICustodyServiceClient _custodyClient;

    public VehicleCustodyService(ICustodyServiceClient custodyClient)
    {
        _custodyClient = custodyClient;
    }

    public async Task<AuthorizationResponse> CreateAuthorizationAsync(
        string vin,
        string origin,
        string destination)
    {
        var request = new CreateAuthorizationRequest
        {
            Vin = vin,
            Origin = origin,
            Destination = destination,
            PersonIdentityKey = "DL-109-283-745",
            MakeModel = "Toyota Camry",
            AuthorizedBy = "SYSTEM",
            Role = AuthorizationRole.DRIVER
        };

        return await _custodyClient.CreateAuthorizationAsync(request);
    }
}

Next Steps