A comprehensive .NET library for interacting with HubSpot APIs.
using DamianH.HubSpot;
// Create a client with your access token
var client = new HubSpotClient("your-access-token");
// Work with CRM objects
var companies = await client.Crm.V3.Objects.Companies.GetAsync();
var contacts = await client.Crm.V3.Objects.Contacts.GetAsync();
// Create a new contact
var newContact = await client.Crm.V3.Objects.Contacts.PostAsync(new SimplePublicObjectInputForCreate
{
Properties = new Dictionary<string, string>
{
["email"] = "test@example.com",
["firstname"] = "John",
["lastname"] = "Doe"
}
});using DamianH.HubSpot.MockServer;
public class MyTests : IAsyncLifetime
{
private HubSpotMockServer _mockServer;
private HubSpotClient _client;
public async Task InitializeAsync()
{
_mockServer = await HubSpotMockServer.StartNew();
_client = new HubSpotClient("mock-token", _mockServer.BaseUri);
}
[Fact]
public async Task CanCreateAndRetrieveContact()
{
// Create contact through mock server
var created = await _client.Crm.V3.Objects.Contacts.PostAsync(new()
{
Properties = new Dictionary<string, string>
{
["email"] = "test@example.com"
}
});
// Retrieve it back
var retrieved = await _client.Crm.V3.Objects.Contacts[created.Id].GetAsync();
Assert.Equal("test@example.com", retrieved.Properties["email"]);
}
public async Task DisposeAsync() => await _mockServer.DisposeAsync();
}- .NET 10.0 SDK or later
- PowerShell (for client generation)
dotnet build hubspot.slndotnet test hubspot.slnAll tests should pass - the test suite validates both the mock server implementation and client functionality.
When HubSpot updates their APIs, regenerate the client:
.\src\generate.ps1Important: Never manually edit files marked with // <auto-generated/> - they will be overwritten during regeneration.
The mock server implements comprehensive support for HubSpot APIs:
- Standard Objects: Companies, Contacts, Deals, Line Items, Products, Tickets, Quotes, Goals, Calls, Emails, Meetings, Notes, Tasks, Postal Mail, Communications
- Custom Objects: Full CRUD + search support
- Associations: Create, read, and manage associations between objects
- Properties: Custom property definitions and management
- Pipelines: Deal and ticket pipelines with stages
- Owners: User and team management
- Imports/Exports: Batch data operations
- Marketing Events: Event management and tracking
- Transactional Email: Send single emails
- Marketing Emails: Campaign email management
- Forms: Form submissions
- Campaigns: Marketing campaign tracking
- Visitor Identification: Track website visitors
- Custom Channels: Custom messaging channels
- Workflows: Automation workflow management
- Blog Posts: Create and manage blog content
- Blog Authors: Author profiles
- Pages: Landing and site pages
- Domains: Domain management
- URL Redirects: Redirect rules
- HubDB: Database tables
- Source Code: File management
- Site Search: Search configuration
- Media Bridge: Media integration
- Webhooks: Subscribe to HubSpot events
- Events: Custom event tracking
- Files: File upload and management
- Business Units: Multi-business unit support
- Schemas: Custom object schemas
- Timeline: Custom timeline events
- Account Info: Account details and configuration
- User Provisioning: User management
- Multicurrency: Currency settings
- Tax Rates: Tax configuration
- Calling: Call integration
- CRM Cards: Custom CRM cards
- Video Conferencing: Meeting integration
- Transcription: Call transcription
The mock server uses an in-memory repository pattern:
- HubSpotMockServer: Main server class that hosts the ASP.NET Core web application
- ApiRoutes.*.cs: Partial classes containing route registrations for each API domain
- Repositories: In-memory stores for different entity types (thread-safe)
- API Models: DTOs matching HubSpot's API contracts
- Stateful: All data persists for the lifetime of the server instance
- Realistic: Mimics HubSpot's actual API behavior (validation, error responses, etc.)
- Testable: Easy to spin up/down for isolated test scenarios
- Extensible: Add new endpoints by extending ApiRoutes partial classes
// ApiRoutes.MyNewApi.cs
public partial class ApiRoutes
{
public static void RegisterMyNewApi(WebApplication app)
{
var group = app.MapGroup("/my-api/v1");
group.MapGet("/items", (MyRepository repo) =>
{
return Results.Ok(repo.GetAll());
});
group.MapPost("/items", (MyRepository repo, MyItem item) =>
{
var created = repo.Create(item);
return Results.Created($"/my-api/v1/items/{created.Id}", created);
});
}
}
// In HubSpotMockServer.cs constructor:
ApiRoutes.RegisterMyNewApi(app);The test suite covers:
- All CRM standard objects (CRUD + search)
- Custom objects and schemas
- Associations between objects
- Batch operations
- Marketing emails and events
- Webhooks and events
- File operations
- CMS functionality
- Business unit support
- Multi-version API support (V3, V202509, etc.)
Tests validate both:
- Mock server behavior (correct responses, validation, etc.)
- Generated Kiota client compatibility
The mock server accepts any authentication token - it doesn't validate tokens. This allows for easy testing without real HubSpot credentials.
For production use with the real HubSpot API:
var client = new HubSpotClient("your-production-access-token");The mock server runs on a random port for each test to avoid conflicts. Access the URI via:
Console.WriteLine($"Mock server running at: {_mockServer.Uri}");Each test should create its own HubSpotMockServer instance to ensure isolation:
public async Task InitializeAsync()
{
_mockServer = await HubSpotMockServer.StartNew();
}The mock server supports HubSpot's search API with filters, sorting, and pagination:
var searchRequest = new PublicObjectSearchRequest
{
FilterGroups = new List<FilterGroup>
{
new FilterGroup
{
Filters = new List<Filter>
{
new Filter { PropertyName = "email", Operator = "EQ", Value = "test@example.com" }
}
}
}
};
var results = await client.Crm.V3.Objects.Contacts.Search.PostAsync(searchRequest);Create associations between objects:
// Associate a contact with a company
await client.Crm.V4.Objects.Contacts[contactId].Associations.Companies[companyId]
.PutAsync(new[]
{
new AssociationSpec { AssociationTypeId = 1 } // contact_to_company
});