Quick Start
Get the VECU Custody .NET SDK running in your application in minutes.
What You Will Build
By the end of this guide, you'll have a working integration that can create custody authorizations, wait for credential issuance, and check vehicle releasability.
Prerequisites
Before you begin, ensure you have: - .NET 8.0 or .NET 10 SDK installed - Access to Cox Automotive Artifactory - A token provider for authentication (your auth system that can supply bearer tokens)
Step 1: Configure NuGet Source
The SDK is hosted in Cox Automotive's Artifactory. Add the NuGet source to your project.
Create or update nuget.config in your solution root:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="cai-artifactory" value="https://artifactory.coxautoinc.com/artifactory/api/nuget/cai-nuget" />
</packageSources>
</configuration>
For authenticated access, use environment variables:
export NUGET_ARTIFACTORY_USER="your-username"
export NUGET_ARTIFACTORY_TOKEN="your-token"
Step 2: Install the SDK
dotnet add package CoxAuto.Vecu.CustodySdk
Step 3: Register Custody Client
Register the client using dependency injection. The SDK integrates natively with ASP.NET Core and hosted services.
using CoxAuto.Vecu.CustodySdk.DependencyInjection;
// Register 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;
};
});
Or configure via appsettings.json with a code-based token provider:
{
"CustodyClient": {
"Environment": "Sandbox",
"TimeoutSeconds": 30
}
}
// Register from configuration + token provider callback
builder.Services.AddCustodyClient(
builder.Configuration,
tokenProvider: async (ct) =>
{
var token = await myAuthClient.GetTokenAsync(ct);
return token.AccessToken;
});
Security
Never hardcode credentials in source code. Store any credentials used by your TokenProvider callback (client IDs, secrets, certificates) in Azure Key Vault, AWS Secrets Manager, or environment variables.
Step 4: Create an Authorization
Create an authorization that permits custody transfer for a vehicle:
using CoxAuto.Vecu.CustodySdk.Client;
using CoxAuto.Vecu.CustodySdk.Models.Requests;
using CoxAuto.Vecu.CustodySdk.Models.Enums;
// Inject client via DI
public class CustodyService
{
private readonly ICustodyServiceClient _client;
public CustodyService(ICustodyServiceClient client)
{
_client = client;
}
public async Task CreateAuthorizationAsync()
{
// Create authorization
var authorization = await _client.CreateAuthorizationAsync(new CreateAuthorizationRequest
{
Vin = "9HGBH41JXMN999999",
Origin = "1 Auction Blvd, Bordentown, NJ 08505",
Destination = "200 Motor Ave, Columbus, OH 43230",
PersonIdentityKey = "DL-293-847-561",
MakeModel = "Honda Accord",
AuthorizedBy = "system-integration",
Role = AuthorizationRole.DRIVER,
ValidUntil = DateTimeOffset.UtcNow.AddHours(24)
});
Console.WriteLine($"Authorization ID: {authorization.AuthorizationId}");
Console.WriteLine($"Status: {authorization.Status}");
}
}
Step 5: Check Vehicle Releasability
Verify if a vehicle can be released before creating an authorization:
using CoxAuto.Vecu.CustodySdk.Models.Requests;
// Check releasability
var result = await _client.GetReleasabilityAsync(
"9HGBH41JXMN999999",
"1 Auction Blvd, Bordentown, NJ 08505");
if (result.IsReleasable)
{
Console.WriteLine("Vehicle is releasable");
Console.WriteLine($"Releasability ID: {result.ReleasabilityId}");
}
else
{
Console.WriteLine("Vehicle cannot be released");
foreach (var blocker in result.Blockers)
{
Console.WriteLine($" - {blocker}");
}
}
Complete Example
using CoxAuto.Vecu.CustodySdk.Client;
using CoxAuto.Vecu.CustodySdk.Configuration;
using CoxAuto.Vecu.CustodySdk.DependencyInjection;
using CoxAuto.Vecu.CustodySdk.Exceptions;
using CoxAuto.Vecu.CustodySdk.Models.Enums;
using CoxAuto.Vecu.CustodySdk.Models.Requests;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
// Build host with dependency injection
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddCustodyClient(options =>
{
options.Environment = CustodyEnvironment.Sandbox;
options.TokenProvider = async (ct) =>
{
var token = await myAuthClient.GetTokenAsync(ct);
return token.AccessToken;
};
});
})
.Build();
var client = host.Services.GetRequiredService<ICustodyServiceClient>();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
try
{
var vin = "9HGBH41JXMN999999";
// Step 1: Check releasability
logger.LogInformation("Checking vehicle releasability...");
var releasability = await client.GetReleasabilityAsync(
vin,
"1 Auction Blvd, Bordentown, NJ 08505");
if (!releasability.IsReleasable)
{
logger.LogWarning("Vehicle is not releasable");
foreach (var blocker in releasability.Blockers)
{
logger.LogWarning($" - {blocker}");
}
return;
}
logger.LogInformation("✓ Vehicle is releasable");
// Step 2: Create authorization
logger.LogInformation("Creating authorization...");
var authorization = await client.CreateAuthorizationAsync(new()
{
Vin = vin,
Origin = "1 Auction Blvd, Bordentown, NJ 08505",
Destination = "200 Motor Ave, Columbus, OH 43230",
PersonIdentityKey = "DL-293-847-561",
MakeModel = "Honda Accord",
AuthorizedBy = "console-app",
Role = AuthorizationRole.DRIVER,
ValidUntil = DateTimeOffset.UtcNow.AddHours(24)
});
logger.LogInformation($"✓ Authorization created: {authorization.AuthorizationId}");
logger.LogInformation($" Status: {authorization.Status}");
// Step 3: Wait for activation
logger.LogInformation("Waiting for authorization to become active...");
var activeAuth = await client.WaitForAuthorizationAsync(
authorization.AuthorizationId,
new WaitOptions
{
PollingIntervalSeconds = 5,
TimeoutSeconds = 300
});
if (activeAuth.Status == AuthorizationStatus.RELEASED)
{
logger.LogInformation("✓ Authorization is active");
}
logger.LogInformation("Workflow completed successfully!");
}
catch (VehicleNotReleasableException ex)
{
logger.LogError($"Vehicle not releasable: {ex.Vin}");
foreach (var blocker in ex.Blockers)
{
logger.LogError($" - {blocker}");
}
}
catch (ValidationException ex)
{
logger.LogError($"Validation error: {ex.Message}");
foreach (var (field, errors) in ex.ValidationErrors)
{
logger.LogError($" {field}: {string.Join(", ", errors)}");
}
}
catch (AuthException ex)
{
logger.LogError($"Authentication failed: {ex.Message}");
}
catch (CustodyException ex)
{
logger.LogError($"Custody error: {ex.Message} (Code: {ex.ErrorCode})");
}
finally
{
await host.StopAsync();
host.Dispose();
}
Error Handling
The SDK provides strongly typed exceptions for precise error handling:
using CoxAuto.Vecu.CustodySdk.Exceptions;
try
{
var authorization = await client.CreateAuthorizationAsync(request);
}
catch (VehicleNotReleasableException ex)
{
// Vehicle has blockers preventing release
Console.WriteLine($"Blockers: {string.Join(", ", ex.Blockers)}");
}
catch (DuplicateAuthorizationException ex)
{
// Authorization already exists for this VIN/OD pair
Console.WriteLine($"Existing ID: {ex.ExistingAuthorizationId}");
}
catch (AuthorizationNotFoundException ex)
{
// Authorization not found
Console.WriteLine($"Not found: {ex.AuthorizationId}");
}
catch (ValidationException ex)
{
// Request validation failed
foreach (var (field, errors) in ex.ValidationErrors)
{
Console.WriteLine($"{field}: {string.Join(", ", errors)}");
}
}
catch (AuthException ex)
{
// Authentication failed
Console.WriteLine($"Auth error: {ex.Message}");
}
catch (RateLimitException ex)
{
// Rate limit exceeded
Console.WriteLine($"Retry after: {ex.RetryAfter?.TotalSeconds} seconds");
}
catch (NetworkException ex)
{
// Network or HTTP error (after retries)
Console.WriteLine($"Network error: {ex.StatusCode}");
}
catch (CustodyException ex)
{
// Base exception for all SDK errors
Console.WriteLine($"Error: {ex.Message} (Code: {ex.ErrorCode})");
}
What's Next?
Now that you have a basic integration working:
- Installation - Detailed installation options
- Configuration - Full configuration reference
- ASP.NET Core Integration - Controllers, background services, health checks
- Error Handling - Handle errors gracefully
- Testing - Unit testing with MockCustodyServiceClient
- API Reference - Complete API documentation