Building Custom Connectors
Extend Dime.Sheets by building custom connectors to sync data with any external system using the IConnector interface.
Building Custom Connectors
Dime.Sheets uses an extensible connector framework that allows you to integrate with any external system. If the built-in Business Central connector does not cover your needs, you can build your own.
Architecture overview
The integration system consists of three key components:
IConnector-- The interface that every connector must implement. Defines methods for pushing and pulling each entity type.SyncService-- The orchestrator that resolves connectors from dependency injection, manages sync scheduling, and logs results toSyncLog.IntegrationConfig-- A per-tenant configuration record that stores the connector type, endpoint URL, credentials, sync direction, and additional JSON settings.
The IConnector interface
Every connector implements IConnector, which defines the connector's type identifier and sync methods:
public interface IConnector
{
string Type { get; }
Task<IEnumerable<Project>> PullProjects(IntegrationConfig config);
Task PushProjects(IEnumerable<Project> projects, IntegrationConfig config);
Task<IEnumerable<TaskItem>> PullTasks(IntegrationConfig config);
Task PushTasks(IEnumerable<TaskItem> tasks, IntegrationConfig config);
Task PushTimeEntries(IEnumerable<TimeEntry> entries, IntegrationConfig config);
}The Type property is a plain string (e.g., "business-central", "jira", "custom-erp"). This is intentionally not an enum to allow open-ended extensibility without modifying framework code.
Creating a connector
Step 1: Implement the interface
Create a new class that implements IConnector:
public class JiraConnector : IConnector
{
public string Type => "jira";
public async Task<IEnumerable<Project>> PullProjects(IntegrationConfig config)
{
var baseUrl = config.Url;
var apiKey = config.ApiKey;
// Call Jira API to fetch projects
// Map Jira projects to Dime.Sheets Project model
// Set ExternalId to the Jira project key
}
public async Task PushProjects(IEnumerable<Project> projects, IntegrationConfig config)
{
// Create or update projects in Jira
}
// Implement remaining methods...
}Step 2: Register in dependency injection
In Program.cs, register your connector alongside the existing ones:
builder.Services.AddScoped<IConnector, BusinessCentralConnector>();
builder.Services.AddScoped<IConnector, JiraConnector>();The SyncService receives all registered connectors via IEnumerable<IConnector> and selects the appropriate one based on the IntegrationConfig.Type matching the connector's Type property.
Step 3: Configure per tenant
In the Dime.Sheets UI under Settings > Integrations, administrators create an integration configuration with your connector's type string. The IntegrationConfig model includes a flexible Settings field (stored as JSON) for any additional configuration your connector needs:
public class IntegrationConfig
{
public string Type { get; set; } // e.g., "jira"
public string Url { get; set; } // Base endpoint URL
public string ApiKey { get; set; } // Authentication credential
public string Direction { get; set; } // "push", "pull", or "bidirectional"
public string Settings { get; set; } // Additional JSON settings
}External ID mapping
Bidirectional sync relies on the ExternalId field present on Project and Task models. When pulling records from an external system, set ExternalId to the unique identifier in that system. When pushing, use ExternalId to look up the corresponding record in the target.
Error handling
Connectors should throw descriptive exceptions when operations fail. The SyncService catches these exceptions, logs them to SyncLog, and marks the sync operation as failed. Users can review errors in the Sync Log UI and retry failed operations.
Well-known connector types
The ConnectorTypes class provides constants for built-in types:
public static class ConnectorTypes
{
public const string BusinessCentral = "business-central";
}You can add your own constants here for consistency, but any string value works.