TipsTrade-HMRC
8.0.2
dotnet add package TipsTrade-HMRC --version 8.0.2
NuGet\Install-Package TipsTrade-HMRC -Version 8.0.2
<PackageReference Include="TipsTrade-HMRC" Version="8.0.2" />
<PackageVersion Include="TipsTrade-HMRC" Version="8.0.2" />
<PackageReference Include="TipsTrade-HMRC" />
paket add TipsTrade-HMRC --version 8.0.2
#r "nuget: TipsTrade-HMRC, 8.0.2"
#:package TipsTrade-HMRC@8.0.2
#addin nuget:?package=TipsTrade-HMRC&version=8.0.2
#tool nuget:?package=TipsTrade-HMRC&version=8.0.2
TipsTrade-HMRC
A strongly typed .NET library for interacting with the HMRC APIs.
The package major version follows the .NET target version (e.g. 8.x.x targets net8.0).
The following services are currently supported:
BusinessDetailsMtdCreateTestUserHelloWorldIndividualCalculationsMtdObligationsMtdSelfAssessmentTestSupportMtdSelfEmploymentBusinessMtdTestFraudPreventionVatVatNumber
Breaking changes
v.8.x.x
In order to keep the major version in sync with the .NET target version, this release includes major breaking changes. The following changes were made in 8.10.0:
- Service-based refactor (see below) which removes the old API wrapper layer and introduces service classes for each API.
*Apitypes have been replaced by*Servicetypes (for example,VatApi→VatService,HelloWorldApi→HelloWorldService).- Internal API construction via
ApiFactoryandIClienthas been removed. - Scope helper calls that referenced API types must now reference service types (for example,
Scopes.GetScopes<VatService>()). - Direct integration points that depended on old internal API wrapper classes/interfaces must be updated to use service classes and
HmrcOptions. - DI registration is now available via
ServiceCollectionextension methods (AddHmrc(...)and per-service add methods). - The
AntiFraudnamespace and related types have been replaced by theFraudPreventionnamespace. Fraud prevention is now configured viaHmrcOptions.FraudPreventionConfigusing anIFraudPreventionimplementation (e.g.BatchProcessDirect,DesktopAppDirect). - The
ServerTokenhas been removed. Access tokens must now be obtained viaHmrcOAuthServiceand anIHmrcAccessTokenProviderimplementation. - The
TokenResponse.ExpiresTimestamphas been removed and replaced byTokenResponse.CreatedAtto ensure persistence of the validity period of refresh tokens.
HMRC Developer Account
A HMRC developer account is required - Login Here. To make requests you need to create an application which provides:
- Client ID
- Client secret
You must configure at least one Redirect URI and add API subscriptions. To run tests and the Authentication-Client console app, the solution requires a Redirect URI of https://www.example.com/hmrc/callback.
Creating test users
The solution includes a .NET console app (Authentication-Client) which can run in either the Sandbox (default) or Production environment. Before running, populate user-secrets:
dotnet user-secrets set Production:ClientSecret <Production Client Secret>
dotnet user-secrets set Production:ClientId <Production Client ID>
dotnet user-secrets set Sandbox:ClientSecret <Sandbox Client Secret>
dotnet user-secrets set Sandbox:ClientId <Sandbox Client ID>
dotnet user-secrets -v list
By default Authentication-Client runs in Sandbox. Pass the /production argument to run in Production.
Test Configuration
Tests require the same credentials as Authentication-Client. A hmrc-users.json is required (empty by default) and must be populated with the output of Authentication-Client for each user type:
{
"Agent": {
"User": null,
"Tokens": null
},
"Individual": {
"User": null,
"Tokens": null
},
"Organisation": {
"User": null,
"Tokens": null
}
}
Scopes
Scopes grant access to specific HMRC APIs and are passed to HmrcOAuthService.GetAuthorizationEndpoint. Scopes must also be enabled for your application in the HMRC developer console.
You can reference scope constants directly:
var scopes = new string[] { Scopes.Hello, Scopes.VATRead, Scopes.VATWrite };
In addition, you can use the Scopes.GetScopes helper methods to retrieve scopes for a specific service, or to filter scopes by name or value:
// Get all the possible scopes - you probably won't want to do this in production.
var scopes = Scopes.GetScopes();
// Get all the scopes for the specified service
var scopes = Scopes.GetScopes<VatService>();
// All Scopes in the Scopes class that contain "VAT", e.g. "VATRead", "VATWrite"
var scopes = Scopes.GetScopes(nameFilter: (name) => name.Contains("VAT"));
// All scopes in the Scopes class that are for the VAT service, and contain "read"
var scopes = Scopes.GetScopes<VatService>(valueFilter: (value) => value.Contains("read"));
Dependency Injection & Configuration
Services are registered via IServiceCollection extension methods. Use AddHmrc<TAccessTokenProvider> for full registration, or register individual services with the per-service add methods.
Services that implement IRequiresFraudPrevention require fraud prevention configuration to be provided via HmrcOptions.FraudPreventionConfig - see the Fraud Prevention Headers section below.
Methods in these services contain overloads that accept an optional fraudPreventionConfig parameter, which can be used to provide per-request fraud prevention configuration that overrides the default configuration.
var services = new ServiceCollection();
// Register all HMRC services with a custom access token provider
services.AddHmrc<MyAccessTokenProvider>(options => {
options.ClientId = "Client ID";
options.ClientSecret = "Client secret";
options.IsSandbox = true; // omit or set false for production
options.FraudPreventionConfig = new BatchProcessDirect {
// populate fraud prevention properties here
};
}, (client) => {
client.Timeout = TimeSpan.FromSeconds(30);
});
var provider = services.BuildServiceProvider();
Or register services individually:
services.AddHmrcOAuthService();
services.AddHmrcHttpClient((client) => {
client.Timeout = TimeSpan.FromSeconds(30);
});
services.AddSingleton<IHmrcAccessTokenProvider, MyAccessTokenProvider>();
services.AddSingleton(Options.Create(new HmrcOptions
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
IsSandbox = true
}));
// Alternatively, register a custom access token provider using the extension method
services.AddHmrcAccessTokenProvider<MyAccessTokenProvider>();
// Or register a custom options provider to dynamically supply HmrcOptions
services.AddHmrcOptionsProvider<MyOptionsProvider>();
// Optionally register a tenant provider for multi-tenant applications
services.AddHmrcTenantProvider<MyTenantProvider>();
services.AddBusinessDetailsMtdService();
services.AddCreateTestUserService();
services.AddHelloWorldService();
services.AddIndividualCalculationsMtdService();
services.AddObligationsMtdService();
services.AddSelfAssessmentTestSupportMtdService();
services.AddSelfEmploymentBusinessMtdService();
services.AddTestFraudPreventionService();
services.AddVatService();
services.AddVatNumberService();
Custom options provider
For scenarios where HmrcOptions must be resolved dynamically, implement IHmrcOptionsProvider and register it with AddHmrcOptionsProvider<T>:
public class MyOptionsProvider : IHmrcOptionsProvider {
public Task<HmrcOptions> GetOptionsAsync(CancellationToken cancellationToken = default) {
// Return options resolved from your own store
return Task.FromResult(new HmrcOptions {
ClientId = "...",
ClientSecret = "...",
IsSandbox = false
});
}
}
services.AddHmrcOptionsProvider<MyOptionsProvider>();
The built-in HmrcOptionsProvider class is used by default when options are configured via IOptions<HmrcOptions> (e.g. via AddHmrc).
Tenant resolution
HMRC services use a tenant value when retrieving credentials from IHmrcAccessTokenProvider. Multi-tenant applications can implement IHmrcTenantProvider and register it with AddHmrcTenantProvider<T>:
public class MyTenantProvider : IHmrcTenantProvider {
public Task<string> GetTenantAsync(CancellationToken cancellationToken = default) {
return Task.FromResult("current-tenant");
}
}
services.AddHmrcTenantProvider<MyTenantProvider>();
The tenant can also be set directly on an individual service instance. A non-empty Tenant value takes precedence over the registered tenant provider; when it is null or empty, the provider is used instead:
var vatService = serviceProvider.GetRequiredService<VatService>();
vatService.Tenant = "tenant-override";
var obligations = await vatService.GetObligationsAsync(obRequest);
If no custom tenant provider or service override is supplied, the built-in provider uses the default single-tenant context.
OAuth
To authenticate a user, build the authorization URL and navigate the user to it. After the user authenticates, handle the redirect URI to obtain TokenResponse (access + refresh tokens). HmrcOAuthService exposes GetAuthorizationEndpoint and HandleEndpointResultAsync to help with this flow.
Example (console-style):
private static async Task<TokenResponse> GetAuthCodeAsync(HmrcOAuthService authClient) {
var state = $"{Guid.NewGuid()}";
var scopes = Scopes.GetScopes();
var redirectUrl = new Uri("https://www.example.com/hmrc/callback");
var url = authClient.GetAuthorizationEndpoint(state, redirectUrl, scopes);
Console.WriteLine();
Console.WriteLine("Navigate to the link below, and login:");
Console.WriteLine($"\t{url}");
Console.WriteLine();
Console.WriteLine($"Paste in the '{redirectUrl}' address that you were redirected to:");
var redirectedTo = Console.ReadLine();
Console.WriteLine();
Console.Write("Validating...");
var resp = await authClient.HandleEndpointResultAsync(redirectedTo, state);
Console.WriteLine(" done");
return resp;
}
Refreshing the access token is done via RefreshAccessTokenAsync:
private static async Task RefreshTokenAsync(HmrcOAuthService authClient) {
Console.Write("Enter the refresh token: ");
var refreshToken = Console.ReadLine();
var tokens = await authClient.RefreshAccessTokenAsync(refreshToken, CancellationToken.None);
Console.WriteLine(JsonConvert.SerializeObject(tokens, Formatting.Indented));
}
Invoking service methods
Services are resolved from the DI container. Both synchronous and asynchronous method variants are provided (where appropriate):
var vatService = serviceProvider.GetRequiredService<VatService>();
// Synchronous
var obligations = vatService.GetObligations(obRequest);
// Asynchronous
var obligations = await vatService.GetObligationsAsync(obRequest);
Exception handling
Service methods can throw the following exceptions:
ApiException— the HMRC API returned an error response.ApiException.Messagecontains the core message;ApiException.ApiErrormay containErrorResponseobjects inApiError.Errors.InvalidOperationException— thrown if the service configuration is invalid for the request.
Example:
try {
submitResponse = await vatService.SubmitReturnAsync(submitRequest);
} catch (InvalidOperationException ex) {
var message = ex.Message;
} catch (ApiException ex) {
var message = ex.Message;
var detailedMessage = string.Join("\r\n", ex.ApiError.Errors?.Select(x => x.Message));
}
CreateTestUser service
In Sandbox you must create test users regularly (Sandbox data is cleared periodically). Use the CreateTestUserFactory helpers:
var createTestUser = serviceProvider.GetRequiredService<CreateTestUserService>();
// Create a test organisation user with all the available service types
var orgRequest = CreateTestUserFactory.CreateTestUserFull<CreateOrganisationRequest>();
// Create a test organisation user with only VAT services
var orgVatRequest = CreateTestUserFactory.CreateTestUser<CreateOrganisationRequest>(s => s.Contains("vat"));
var user = await createTestUser.CreateUserAsync(orgRequest);
HelloWorld service
Simple echo endpoints to verify credentials:
var helloWorld = serviceProvider.GetRequiredService<HelloWorldService>();
// Returns "Hello World" - no application or user credentials are required
var resp = await helloWorld.SayHelloWorldAsync();
// Returns "Hello Application" - application credentials are required
var resp = await helloWorld.SayHelloApplicationAsync();
// Returns "Hello User" - a valid user's access token is required
var resp = await helloWorld.SayHelloUserAsync();
VAT (MTD) service
Get obligations
VAT returns are indexed by a "Period Key". ObligationsResult implements IComparable so results can be sorted. Note: HMRC specify that the Period Key should not be shown to end users.
var vatService = serviceProvider.GetRequiredService<VatService>();
var obRequest = new ObligationsRequest() {
Vrn = vrn,
Status = ObligationStatus.Fulfilled, // Use null to request all obligations
DateFrom = DateTime.Today.AddYears(-1),
DateTo = DateTime.Today
};
var obligations = await vatService.GetObligationsAsync(obRequest);
// The most recent fulfilled obligation
var periodKey = obligations.Value?.OrderByDescending(x => x).FirstOrDefault()?.PeriodKey;
Get VAT return
var returnRequest = new ReturnRequest() {
Vrn = vrn,
PeriodKey = periodKey // As retrieved by GetObligations
};
var vatReturn = await vatService.GetReturnAsync(returnRequest);
Submit VAT return
Decimal values should have no more than two decimal places (use Math.Round).
var vatReturn = new VatReturn() {
PeriodKey = periodKey,
VatDueSales = 7724.92m,
VatDueAcquisitions = 703.49m,
TotalVatDue = 7724.92m + 703.49m,
VatReclaimedCurrPeriod = 1681.08m,
NetVatDue = 7724.92m + 703.49m - 1681.08m,
TotalValueSalesExVAT = 38622,
TotalValuePurchasesExVAT = 8405,
TotalValueGoodsSuppliedExVAT = 3703,
TotalAcquisitionsExVAT = 500,
Finalised = true // Must be true for the return to be accepted
};
var request = new SubmitRequest() {
Return = vatReturn,
Vrn = vrn,
};
var resp = await vatService.SubmitReturnAsync(request);
Other methods
Other VAT methods: GetLiabilities, GetPayments - see API docs in code.
Fraud prevention headers
Certain services require fraud prevention headers. Configure them via HmrcOptions.FraudPreventionConfig using an IFraudPrevention implementation from the TipsTrade.HMRC.FraudPrevention.ConnectionMethods namespace.
Available connection method types:
BatchProcessDirectDesktopAppDirectDesktopAppViaServerMobileAppDirectMobileAppViaServerOtherDirectOtherViaServerWebAppViaServer
Each type implements one or more header interfaces (e.g. IDeviceId, ILocalIps, IMacAddresses, IUserAgent, IScreens, ITimeZone, IVendorVersion, IWindowSize) which expose the relevant properties. Helper extension methods are available on several interfaces:
// Populates with all the local IP addresses on the local system
localIps.PopulateLocalIps();
// Populates with all the local IP addresses that match a predicate
localIps.PopulateLocalIps(ip => ip.AddressFamily == AddressFamily.InterNetwork);
// Populates with all the MAC addresses on the local system
macAddresses.PopulateMacAddresses();
// Populates with the local operating system name and version, and device details
userAgent.PopulateUserAgent("Dell", "XPS");
Example configuration:
options.FraudPreventionConfig = new BatchProcessDirect {
DeviceId = Guid.Parse("your-device-guid"),
TimeZone = TimeZoneInfo.Local,
VendorVersion = new Dictionary<string, string>() {
{ "MyApp", "1.0.0.0" }
}
};
// Populate system values via interface checks
if (options.FraudPreventionConfig is ILocalIps localIps)
localIps.PopulateLocalIps();
if (options.FraudPreventionConfig is IMacAddresses macAddresses)
macAddresses.PopulateMacAddresses();
if (options.FraudPreventionConfig is IUserAgent userAgent)
userAgent.PopulateUserAgent("MyManufacturer", "MyProduct");
TestFraudPrevention service
The TestFraudPreventionService can be used in Sandbox to validate your fraud prevention headers against HMRC's spec, and to retrieve per-service feedback.
var testFraudPrevention = serviceProvider.GetRequiredService<TestFraudPreventionService>();
// Validate the currently configured fraud prevention headers
var result = await testFraudPrevention.ValidateAsync();
if (result.Errors?.Any() == true) {
// Headers are invalid - inspect result.Errors for details
}
// Get feedback for a specific service (e.g. "vat-mtd")
var feedback = await testFraudPrevention.GetFeedbackAsync("vat-mtd");
| 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. |
| .NET Framework | net481 is compatible. |
-
.NETFramework 4.8.1
- AsyncKeyedLock (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Options (>= 10.0.8)
- Newtonsoft.Json (>= 13.0.4)
- RestSharp (>= 114.0.0)
- System.Text.Json (>= 10.0.8)
- TipsTrade.ApiClient.Core (>= 8.0.0)
-
net8.0
- AsyncKeyedLock (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Options (>= 10.0.8)
- Newtonsoft.Json (>= 13.0.4)
- RestSharp (>= 114.0.0)
- System.Text.Json (>= 10.0.8)
- TipsTrade.ApiClient.Core (>= 8.0.0)
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 |
|---|---|---|
| 8.0.2 | 55 | 7/21/2026 |
| 8.0.1 | 103 | 7/3/2026 |
| 8.0.0-beta6 | 102 | 6/4/2026 |
| 0.0.5-beta1 | 98 | 5/20/2026 |
| 0.0.4-beta1 | 96 | 5/13/2026 |
| 0.0.4-beta.1 | 55 | 5/13/2026 |
| 0.0.4-beta | 93 | 5/13/2026 |
| 0.0.3.4 | 142 | 3/17/2026 |
| 0.0.3.3 | 110 | 3/17/2026 |
| 0.0.3.1 | 253 | 9/3/2024 |
| 0.0.2.10 | 672 | 5/20/2021 |
| 0.0.2.8 | 688 | 12/9/2020 |
| 0.0.2.7 | 730 | 2/25/2020 |