API Reference
Complete reference for the InteropSuite public API.
Overview
The InteropSuite API is designed for simplicity. The primary entry point is the
static Interop class which provides methods for transforming healthcare
messages to FHIR R4 bundles.
All methods in the Interop class are thread-safe and can be called concurrently from multiple threads.
Namespace
using InteropSuite.Fhir.Engine;
NuGet Package
dotnet add package InteropSuite
Interop Class
The main entry point for all transformations.
HL7ToFhirAsync
Transforms an HL7 v2.x message to a FHIR R4 bundle.
public static Task<ConversionResult> HL7ToFhirAsync(
string payload,
IProgress<TransformProgress>? progress = null,
CancellationToken cancellationToken = default
)
| Parameter | Type | Description |
|---|---|---|
| payload | string | Raw HL7 v2.x message content |
| progress | IProgress<TransformProgress>? | Optional progress reporter for real-time status updates |
| cancellationToken | CancellationToken | Optional cancellation token |
Returns: ConversionResult containing the FHIR bundle or errors.
X12ToFhirAsync
Transforms an X12 5010 transaction to a FHIR R4 bundle.
public static Task<ConversionResult> X12ToFhirAsync(
string payload,
OutputFormat output = OutputFormat.UsCore,
IProgress<TransformProgress>? progress = null,
CancellationToken cancellationToken = default
)
| Parameter | Type | Description |
|---|---|---|
| payload | string | Raw X12 EDI content |
| output | OutputFormat | US Core (default) or CMS-0057-F profiles |
| progress | IProgress<TransformProgress>? | Optional progress reporter for real-time status updates |
| cancellationToken | CancellationToken | Optional cancellation token |
Returns: ConversionResult containing the FHIR bundle or errors.
CDAToFhirAsync
Transforms a C-CDA R2.1 document to a FHIR R4 bundle.
public static Task<ConversionResult> CDAToFhirAsync(
string payload,
IProgress<TransformProgress>? progress = null,
CancellationToken cancellationToken = default
)
| Parameter | Type | Description |
|---|---|---|
| payload | string | Raw C-CDA XML content |
| progress | IProgress<TransformProgress>? | Optional progress reporter. Useful for large CDA files with chunked processing. |
| cancellationToken | CancellationToken | Optional cancellation token |
Returns: ConversionResult containing the FHIR bundle or errors.
C-CDA documents containing embedded binary media (PDFs, images via <observationMedia>,
or unstructured content in <nonXMLBody>) are automatically skipped during batch processing.
InteropSuite transforms structured clinical data only.
ToFhirAsync (Auto-Detect)
Transforms any supported format to FHIR R4, auto-detecting the input type.
public static Task<ConversionResult> ToFhirAsync(
string payload,
OutputFormat output = OutputFormat.UsCore,
IProgress<TransformProgress>? progress = null,
CancellationToken cancellationToken = default
)
| Parameter | Type | Description |
|---|---|---|
| payload | string | Raw message content (HL7, X12, or C-CDA) |
| output | OutputFormat | Output profile (only affects X12) |
| progress | IProgress<TransformProgress>? | Optional progress reporter for real-time status updates |
| cancellationToken | CancellationToken | Optional cancellation token |
Format Detection:
- HL7 v2.x: Message starts with
MSH| - X12: Message starts with
ISA* - C-CDA: XML with
<ClinicalDocument>root element
Synchronous Versions
All async methods have synchronous counterparts:
// Synchronous versions
ConversionResult HL7ToFhir(string payload)
ConversionResult X12ToFhir(string payload, OutputFormat output = OutputFormat.UsCore)
ConversionResult CDAToFhir(string payload)
ConversionResult ToFhir(string payload, OutputFormat output = OutputFormat.UsCore)
Interop Properties
Static properties on the Interop class for configuration and diagnostics.
Set configuration properties at application startup before any API calls.
Dashboard Properties
| Property | Type | Default | Description |
|---|---|---|---|
DashboardEnabled |
bool | false | Enables dashboard data generation. When enabled, all batch conversions are logged to the dashboard directory for TraceServer visualization. |
DashboardRetentionDays |
int | 7 | Number of days to retain dashboard session data. Sessions older than this are automatically deleted. Set to 0 to disable cleanup. |
DashboardDirectory |
string | read-only | Gets the dashboard directory path. Default: {workingDir}/interopsuite/dashboard |
Path Properties (Read-Only)
| Property | Type | Default Location | Description |
|---|---|---|---|
DataRoot |
string | {workingDir}/interopsuite |
Root directory for all InteropSuite data. Override with SetDataRoot() or INTEROPSUITE_ROOT env var. |
AuditDirectory |
string | {DataRoot}/audit |
Directory for audit log files. Override with INTEROPSUITE_AUDIT_DIR env var. |
QuarantineDirectory |
string | {DataRoot}/quarantine |
Directory for quarantined messages (AES-256-GCM encrypted). Override with INTEROPSUITE_QUARANTINE_DIR env var. |
OutputDirectory |
string | {DataRoot}/output/fhir |
Directory for FHIR bundle output from batch processing. Override with INTEROPSUITE_OUTPUT_DIR env var. |
Example: Configure at Startup
using InteropSuite.Fhir.Engine;
// Configure paths and dashboard at startup
Interop.SetDataRoot("/var/interopsuite");
Interop.DashboardEnabled = true;
Interop.DashboardRetentionDays = 14;
// Activate license
Interop.ActivateLicense("YOUR-LICENSE-KEY");
// Check configured paths
Console.WriteLine(Interop.GetPathsSummary());
// Output:
// DataRoot: /var/interopsuite
// Dashboard: /var/interopsuite/dashboard
// Audit: /var/interopsuite/audit
// Quarantine: /var/interopsuite/quarantine
// Output: /var/interopsuite/output/fhir
Path Configuration
Methods to configure data directories at runtime. Call these at application startup before any processing.
SetDataRoot
Sets the root directory for all InteropSuite data. All other paths are relative to this root.
public static void SetDataRoot(string path)
| Parameter | Type | Description |
|---|---|---|
| path | string | Absolute path to the root directory |
Directory structure created:
{path}/
├── dashboard/ ← TraceServer sessions
├── audit/ ← Audit logs
├── quarantine/ ← Encrypted quarantine
└── output/
└── fhir/ ← FHIR bundles
├── validated/ ← Passed validation
└── errors/ ← Had validation errors
ResetDataRoot
Resets the data root to default ({workingDir}/interopsuite or INTEROPSUITE_ROOT env var).
public static void ResetDataRoot()
GetPathsSummary
Returns a formatted summary of all configured paths for diagnostics and logging.
public static string GetPathsSummary()
Reset
Resets the internal transformation engine. Useful for testing or after license changes.
public static void Reset()
Configure paths before activating your license. Environment variables take precedence over defaults but SetDataRoot() takes precedence over everything.
Cancellation Tokens
All async methods accept an optional CancellationToken parameter. This is a .NET mechanism
for cooperative cancellation of long-running async operations. It allows you to gracefully stop
processing when needed.
What is a CancellationToken?
A CancellationToken is passed into async methods and signals when cancellation has been
requested. The method periodically checks the token and stops processing if cancellation is requested.
This is useful for:
- Timeouts — Stop processing if it takes too long
- User cancellation — Allow users to cancel via a "Stop" button
- Application shutdown — Gracefully stop when the app is closing
- Request cancellation — In web APIs, handle client disconnection
Basic Usage (Without Cancellation)
If you don't need cancellation, simply omit the parameter:
// No cancellation - runs to completion
var result = await Interop.HL7ToFhirAsync(message);
Timeout Example
Cancel the operation if it takes longer than a specified time:
// Create a token that cancels after 30 seconds
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
var result = await Interop.HL7ToFhirAsync(message, cts.Token);
// Process result...
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation timed out after 30 seconds");
}
Manual Cancellation Example
Cancel when a user clicks a button or an event occurs:
// Create a token source that can be cancelled manually
private CancellationTokenSource? _cts;
public async Task StartProcessingAsync()
{
_cts = new CancellationTokenSource();
try
{
var result = await Interop.CDAToFhirAsync(largeDocument, _cts.Token);
// Process result...
}
catch (OperationCanceledException)
{
Console.WriteLine("Processing was cancelled by user");
}
}
public void CancelProcessing()
{
_cts?.Cancel();
}
ASP.NET Core Web API Example
In web APIs, use the request's cancellation token to handle client disconnection:
[HttpPost("transform")]
public async Task<IActionResult> Transform(
[FromBody] string message,
CancellationToken cancellationToken) // ASP.NET injects this
{
var result = await Interop.ToFhirAsync(message, cancellationToken: cancellationToken);
if (result.Success)
return Ok(result.FhirBundle);
return BadRequest(result.Errors);
}
Always pass cancellation tokens through your call chain. In web APIs, the request token automatically cancels when the client disconnects, saving server resources.
Batch Processing
Process thousands of files efficiently with InteropSuite's dual-queue architecture. The batch processor automatically handles large files (like C-CDA documents) with memory-aware throttling while processing smaller files in parallel.
Files are automatically sorted into two queues: Light Queue for small files (fast parallel processing) and Heavy Queue for large files (memory-aware throttling). This prevents memory exhaustion when processing large C-CDA documents or mixed workloads.
Two Processing Modes
InteropSuite supports two batch processing modes:
| Mode | Use Case | Behavior |
|---|---|---|
| Sequential | Process one directory at a time | Each batch completes before the next starts |
| Unified (RoundRobin) Recommended | Process multiple directories simultaneously | Files interleaved across sources - no domain blocks another |
Process one directory at a time. Each batch completes before the next starts.
public static Task<BatchResult> ProcessBatchAsync(
string inputDirectory,
BatchOptions options,
CancellationToken cancellationToken = default
)
| Parameter | Type | Description |
|---|---|---|
| inputDirectory | string | Path to directory containing input files |
| options | BatchOptions | Batch processing configuration |
| cancellationToken | CancellationToken | Optional cancellation token |
Sequential Example
using InteropSuite.Fhir.Engine;
// Each call processes one directory - batches run sequentially
await Interop.ProcessBatchAsync("/input/hl7", new BatchOptions { Domain = "HL7" });
await Interop.ProcessBatchAsync("/input/x12", new BatchOptions { Domain = "X12" });
await Interop.ProcessBatchAsync("/input/cda", new BatchOptions { Domain = "CDA" });
await Interop.ProcessBatchAsync("/input/cms", new BatchOptions
{
Domain = "CMS-0057-F",
OutputFormat = OutputFormat.Cms0057F
});
// HL7 finishes → X12 finishes → CDA finishes → CMS finishes (sequential)
// Creates separate TraceServer sessions per batch
Process multiple directories in a single call with RoundRobin interleaving. No domain blocks another. This is the recommended approach for production workloads.
public static Task<BatchResult> ProcessBatchAsync(
BatchOptions options,
CancellationToken cancellationToken = default
)
Use the Sources property to specify multiple input directories:
Unified Example
using InteropSuite.Fhir.Engine;
var options = new BatchOptions
{
Sources = new[]
{
new BatchSource("/input/hl7", "HL7", OutputFormat.UsCore),
new BatchSource("/input/x12", "X12", OutputFormat.UsCore),
new BatchSource("/input/cda", "CDA", OutputFormat.UsCore),
new BatchSource("/input/cms", "CMS-0057-F", OutputFormat.Cms0057F),
}
};
var result = await Interop.ProcessBatchAsync(options);
// Processing order: HL7[0], X12[0], CDA[0], CMS[0], HL7[1], X12[1]...
// Single TraceServer session showing all domains
Console.WriteLine($"Processed: {result.TotalFiles} files");
Console.WriteLine($"Succeeded: {result.Succeeded}");
OutputFormat. Single TraceServer session shows all domains.
BatchSource Class
Defines a source directory with its domain and output format for unified processing.
public sealed class BatchSource
{
public string InputDirectory { get; }
public string Domain { get; }
public OutputFormat OutputFormat { get; }
public BatchSource(string inputDirectory, string domain, OutputFormat outputFormat);
}
| Property | Type | Description |
|---|---|---|
| InputDirectory | string | Path to input files for this source |
| Domain | string | Domain identifier for TraceServer (e.g., "HL7", "X12", "CDA") |
| OutputFormat | OutputFormat | UsCore or Cms0057F for this source |
Sequential vs Unified Comparison
| Aspect | Sequential | Unified (RoundRobin) |
|---|---|---|
| API | ProcessBatchAsync(string, BatchOptions) | ProcessBatchAsync(BatchOptions) with Sources |
| Processing | One domain at a time | Interleaved across all domains |
| Blocking | Domain A blocks Domain B | No blocking between domains |
| TraceServer | Separate sessions per batch | Single unified session |
| OutputFormat | Global per batch | Per-source configurable |
Use unified mode for all production workloads. It provides fair scheduling across sources, prevents large batches from blocking other formats, and gives you a single TraceServer view of all processing activity.
BatchOptions
Configuration options for batch processing with parallel workers and memory management.
| Property | Type | Default | Description |
|---|---|---|---|
| Sources | BatchSource[]? | null | Multi-source configuration for unified mode |
| Domain | string? | null | Filter: "HL7", "X12", "CDA", or null for auto-detect (single-source mode) |
| OutputFormat | OutputFormat | UsCore | Output profile (UsCore or Cms0057F) for single-source mode |
| Workers | int? | auto | Parallel workers for small files (auto = CPU cores / 2, min 2, max 8) |
| HeavyWorkers | int? | auto | Workers for large files (auto = Workers / 2, min 1, max 4) |
| LargeFileSizeThreshold | long | 1MB | Files above this size use heavy queue with memory throttling |
| MaxFileSizeBytes | long | 50MB | Maximum file size allowed (50MB hard cap). Files exceeding this limit are skipped. Values above 50MB are automatically capped. |
| EnableCheckpoints | bool | false | Enable pause/resume for very large batches |
| ContinueOnError | bool | true | Continue processing if individual files fail |
| FileExtensions | string[] | .hl7, .x12, ... | File extensions to process |
Benchmark Results
InteropSuite was tested with 1,536 synthetic healthcare files across all supported formats using batch processing in unified mode (RoundRobin interleaving across HL7, X12, CMS-0057-F, and CDA sources). Performance varies by file size — results below are grouped by size ranges for meaningful comparison. Most failures encountered were due to source data quality issues (invalid codes, missing required fields) rather than transformation errors.
HL7 v2.x Performance
49 files processed (40 quarantined due to unsupported message types like QRY, BAR).
| File Size | Files | Pass Rate | Avg Duration | Resources |
|---|---|---|---|---|
| < 1 KB | 33 | 81.8% | 1,408 ms | 140 |
| 1 - 10 KB | 14 | 85.7% | 223 ms | 262 |
| 10 - 100 KB | 2 | 100% | 148 ms | 4 |
X12 5010 Performance
192 files processed (US Core compliance).
| File Size | Files | Pass Rate | Avg Duration | Resources |
|---|---|---|---|---|
| < 1 KB | 164 | 100% | 271 ms | 734 |
| 1 - 10 KB | 28 | 92.9% | 115 ms | 283 |
X12 CMS-0057-F Performance
563 files processed (Da Vinci PAS / CARIN Blue Button compliance).
| File Size | Files | Pass Rate | Avg Duration | Resources |
|---|---|---|---|---|
| < 1 KB | 563 | 100% | 160 ms | 4,074 |
C-CDA R2.1 Performance
692 files processed using chunked memory-safe processing for large files.
| File Size | Files | Pass Rate | Avg Duration | Resources |
|---|---|---|---|---|
| 1 - 10 KB | 12 | 100% | 7.8 sec | 0 |
| 10 - 100 KB | 123 | 100% | 18.9 sec | 2,959 |
| 100 KB - 1 MB | 458 | 99.8% | 31.4 sec | 223,077 |
| 1 - 10 MB | 94 | 98.9% | 1.3 min | 143,069 |
| 10 - 50 MB | 5 | 80% | 27.3 min | 28,619 |
Largest C-CDA Files Processed
| File Size | Duration | Resources | Status |
|---|---|---|---|
| 45.0 MB | 27.9 min | 10,416 | ✔ Passed |
| 18.1 MB | 16.5 min | 8,291 | ✔ Passed |
| 13.5 MB | 15.5 min | 5,406 | ✔ Passed |
| 11.6 MB | 7.4 min | 4,506 | ✔ Passed |
| 9.1 MB | 6.3 min | 3,959 | ✔ Passed |
InteropSuite has been tested with C-CDA files up to 45 MB using chunked processing. The 50MB limit is the maximum supported file size to prevent memory exhaustion. Files exceeding this limit are automatically skipped during batch processing.
High-Performance Example (Large C-CDA Files)
When processing large C-CDA documents (often 1-10MB each), configure for optimal memory usage:
var options = new BatchOptions
{
Domain = "CDA",
Workers = 4, // 4 workers for small files
HeavyWorkers = 2, // 2 workers for large files (memory-aware)
LargeFileSizeThreshold = 512 * 1024, // 512KB threshold
MaxFileSizeBytes = 50 * 1024 * 1024, // 50MB max (hard cap)
EnableCheckpoints = true // Enable resume on failure
};
using var cts = new CancellationTokenSource();
// Allow graceful shutdown on Ctrl+C
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};
var result = await Interop.ProcessBatchAsync("./cda-input", options, cts.Token);
Console.WriteLine($"Files: {result.Succeeded}/{result.TotalFiles}");
Console.WriteLine($"Resources: {result.TotalResources:N0}");
Console.WriteLine($"Throughput: {result.TotalFiles / result.Duration.TotalMinutes:F1} files/min");
Mixed Workload Example (Unified)
Process HL7, X12, and C-CDA files together with RoundRobin scheduling:
var options = new BatchOptions
{
Sources = new[]
{
new BatchSource("/data/hl7-messages", "HL7", OutputFormat.UsCore),
new BatchSource("/data/x12-claims", "X12", OutputFormat.UsCore),
new BatchSource("/data/cda-documents", "CDA", OutputFormat.UsCore),
new BatchSource("/data/cms-claims", "CMS-0057-F", OutputFormat.Cms0057F),
},
Workers = 6,
HeavyWorkers = 2,
LargeFileSizeThreshold = 1024 * 1024 // 1MB
};
var result = await Interop.ProcessBatchAsync(options);
Console.WriteLine($"Total: {result.TotalFiles}, Succeeded: {result.Succeeded}");
BatchResult
Results returned from batch processing.
| Property | Type | Description |
|---|---|---|
| TotalFiles | int | Total number of files processed |
| Succeeded | int | Number of files successfully transformed |
| Failed | int | Number of files that failed |
| Skipped | int | Number of files skipped due to exceeding MaxFileSizeBytes limit |
| TotalResources | int | Total FHIR resources created |
| Duration | TimeSpan | Total processing time |
FHIR bundles are written to output/fhir/ organized by date. Each bundle is named
after the source file with a .json extension. Failed files are logged for review.
Large C-CDA files (5-10MB) can consume significant memory during parsing. The heavy queue
automatically applies GC pressure and throttling. For very large batches, consider reducing
HeavyWorkers to 1-2 and enabling checkpoints.
Batch Classes
Supporting classes for batch processing results and progress reporting.
BatchFileResult
Contains the result for an individual file processed in a batch. Access via BatchResult.FileResults.
| Property | Type | Description |
|---|---|---|
FilePath | string | Full path to the source file |
FileName | string | File name only (without path) |
Success | bool | Whether the file was processed successfully |
MessageType | string? | Detected message type (e.g., "X12 837P", "HL7 ADT^A01", "CDA 34133-9") |
ResourceCount | int | Number of FHIR resources generated |
Duration | TimeSpan | Processing time for this file |
ErrorMessage | string? | Error message if processing failed |
WasQuarantined | bool | Whether the file was quarantined (invalid/malformed) |
OutputPath | string? | Relative path to output FHIR bundle file |
SourceSize | long | Source file size in bytes |
ValidationErrorCount | int | Number of FHIR validation errors. Zero = validated/ folder, >0 = errors/ folder |
BatchResult Additional Properties
Computed properties available on BatchResult for statistics:
| Property | Type | Description |
|---|---|---|
Quarantined | int | Number of files that were quarantined |
AverageMillisecondsPerFile | double | Average processing time per file |
FilesPerSecond | double | Processing throughput |
SuccessRate | double | Success percentage (0-100) |
InputDirectory | string? | The input directory that was processed |
OutputDirectory | string? | The output directory where bundles were written |
BatchProgress
Progress information passed to IProgress<BatchProgress> callback during batch processing.
| Property | Type | Description |
|---|---|---|
TotalFiles | int | Total number of files to process |
ProcessedFiles | int | Number of files processed so far |
SucceededFiles | int | Number of successful files so far |
FailedFiles | int | Number of failed files so far |
CurrentFile | string? | Name of the file currently being processed |
PercentComplete | double | Progress percentage (0-100) |
TransformProgress
Progress information passed to IProgress<TransformProgress> callback during single-file transformations. Useful for large CDA documents that use chunked processing.
| Property | Type | Description |
|---|---|---|
Phase | TransformPhase | Current processing phase |
Message | string | Human-readable message describing current activity |
PercentComplete | double | Progress percentage (0-100) |
CurrentChunk | int | For chunked CDA: current chunk number (1-based) |
TotalChunks | int | For chunked CDA: total number of chunks |
TotalEntries | int | For chunked CDA: total clinical entries in the document |
ProcessedEntries | int | For chunked CDA: entries processed so far |
ResourcesCreated | int | Number of FHIR resources created so far |
Elapsed | TimeSpan | Elapsed time since processing started |
TransformPhase Enum
Processing phases reported by TransformProgress:
| Value | Description |
|---|---|
Detecting | Detecting input format (HL7, X12, CDA) |
Parsing | Parsing input message |
Validating | Validating input against rules |
Transforming | Transforming to canonical model |
BuildingFhir | Building FHIR resources |
ProcessingChunk | Processing chunk (for large CDA documents) |
MergingChunks | Merging chunks (for large CDA documents) |
ValidatingFhir | Validating FHIR output |
Complete | Processing complete |
Single-File Progress Example
// Create progress reporter for single-file transformations
var progress = new Progress<TransformProgress>(p =>
{
var phase = p.Phase.ToString().PadRight(16);
var percent = $"{p.PercentComplete,5:F1}%";
if (p.Phase == TransformPhase.ProcessingChunk)
{
// Show chunk progress for large CDA documents
Console.WriteLine($"[{percent}] {phase} Chunk {p.CurrentChunk}/{p.TotalChunks}");
}
else
{
Console.WriteLine($"[{percent}] {phase} {p.Message}");
}
});
var result = await Interop.CDAToFhirAsync(largeCdaPayload, progress);
Batch Progress Reporting Example
var options = new BatchOptions { Domain = "CDA" };
// Create progress reporter
var progress = new Progress<BatchProgress>(p =>
{
Console.Write($"\r[{p.PercentComplete:F1}%] {p.ProcessedFiles}/{p.TotalFiles}");
if (p.CurrentFile != null)
Console.Write($" - {p.CurrentFile}");
});
var result = await Interop.ProcessBatchAsync("./input", options, progress);
Console.WriteLine();
Console.WriteLine($"Complete: {result.SuccessRate}% success rate");
Console.WriteLine($"Throughput: {result.FilesPerSecond:F1} files/sec");
Iterating File Results
var result = await Interop.ProcessBatchAsync("./input", options);
// Summary statistics
Console.WriteLine($"Processed: {result.TotalFiles} files");
Console.WriteLine($"Success rate: {result.SuccessRate}%");
Console.WriteLine($"Avg time: {result.AverageMillisecondsPerFile:F0}ms/file");
// Iterate individual file results
foreach (var file in result.FileResults)
{
if (file.Success)
{
Console.WriteLine($"✓ {file.FileName}: {file.ResourceCount} resources");
}
else
{
Console.WriteLine($"✗ {file.FileName}: {file.ErrorMessage}");
}
}
// Find files with validation errors
var withErrors = result.FileResults
.Where(f => f.ValidationErrorCount > 0)
.ToList();
Console.WriteLine($"Files with validation errors: {withErrors.Count}");
Dashboard API
The Dashboard API enables real-time visibility into batch processing via TraceServer. When enabled, InteropSuite writes session data that can be viewed in a web-based dashboard.
Enabling the Dashboard
using InteropSuite.Fhir.Engine;
// Enable at application startup
Interop.DashboardEnabled = true;
Interop.DashboardRetentionDays = 7; // Optional: keep 7 days of history
// Run batch processing - data appears in dashboard
var result = await Interop.ProcessBatchAsync(options);
// View dashboard: run TraceServer and open http://localhost:8765
Console.WriteLine($"Dashboard: {Interop.DashboardDirectory}");
Dashboard Data Flow
| Processing Mode | Session Tracking | TraceServer Display |
|---|---|---|
ToFhirAsync(string) |
Not tracked | String API is ephemeral, no dashboard entry |
ProcessBatchAsync(dir, options) |
Named session by domain | Full file-by-file tracking with progress |
ProcessBatchAsync(options) with Sources |
Unified session | All domains in single view |
Dashboard Directory Structure
{DashboardDirectory}/
├── aggregate.json ← Aggregated data (read by TraceServer)
├── sessions/
│ ├── HL7_{timestamp}.json ← HL7 batch session
│ ├── X12_{timestamp}.json ← X12 batch session
│ ├── CDA_{timestamp}.json ← CDA batch session
│ └── CMS-0057-F_{timestamp}.json
└── server.pid ← TraceServer process ID
Running TraceServer
TraceServer is a standalone executable that serves the dashboard web interface:
# Run TraceServer pointing to dashboard directory
./TraceServer -d /path/to/interopsuite/dashboard
# Or with default dashboard location
./TraceServer
# Open browser to http://localhost:8765
TraceServer uses Server-Sent Events (SSE) to push updates to the browser in real-time. You can watch files being processed as they complete, with success/failure status and resource counts.
ConversionResult
Contains the result of a transformation operation.
Properties
| Property | Type | Description |
|---|---|---|
| Success | bool | True if transformation completed successfully |
| FhirBundle | string? | JSON-serialized FHIR Bundle (null if failed) |
| DetectedFormat | string? | Detected message type (e.g., "ADT^A01", "X12^837P") |
| OutputMode | OutputFormat | Profile mode used (UsCore or Cms0057F) |
| Errors | List<string> | List of error messages (blocking issues) |
| Warnings | List<string> | List of warning messages (non-blocking) |
| ResourceCount | int | Number of resources in the bundle |
Usage Example
var result = await Interop.HL7ToFhirAsync(message);
if (result.Success)
{
Console.WriteLine($"Format: {result.DetectedFormat}");
Console.WriteLine($"Resources: {result.ResourceCount}");
Console.WriteLine($"Bundle: {result.FhirBundle}");
// Check for warnings
foreach (var warning in result.Warnings)
Console.WriteLine($"Warning: {warning}");
}
else
{
// Handle errors
foreach (var error in result.Errors)
Console.WriteLine($"Error: {error}");
}
OutputFormat Enum
Specifies the FHIR profile set to use for output.
| Value | Description | Applies To |
|---|---|---|
UsCore |
US Core 6.1.0 profiles (default) | All formats |
Cms0057F |
CMS-0057-F compliant profiles (CARIN, Da Vinci PAS, PDex) | X12 only |
CMS-0057-F mode only affects X12 transactions. HL7 and C-CDA always use US Core profiles.
License Management
Methods for activating and managing your InteropSuite license.
ActivateLicense
Activates the license using a Base64-encoded license key.
public static void ActivateLicense(string licenseKey, bool verbose = false)
Call this once at application startup:
// In Program.cs or Startup.cs
Interop.ActivateLicense("your-base64-license-key");
// Or with verbose output for troubleshooting
Interop.ActivateLicense("your-base64-license-key", verbose: true);
ActivateLicenseFromFile
Activates the license from a file.
public static void ActivateLicenseFromFile(string filePath)
// Load from file
Interop.ActivateLicenseFromFile("interopsuite.lic");
ActivateLicenseAuto
Auto-detects license from environment variable or file.
public static void ActivateLicenseAuto()
Searches in order:
INTEROPSUITE_LICENSEenvironment variableinteropsuite.licfile in current directoryinteropsuite.licfile in app directory
GetLicenseInfo
Returns information about the active license.
public static LicenseInfo? GetLicenseInfo()
var info = Interop.GetLicenseInfo();
if (info != null)
{
Console.WriteLine($"Organization: {info.Organization}");
Console.WriteLine($"Expires: {info.Expires}");
Console.WriteLine($"Products: {info.Products.Count}");
}
License Classes
Classes returned by GetLicenseInfo() for inspecting the active license.
LicenseInfo
Public license information (safe to expose). Returned by Interop.GetLicenseInfo().
| Property | Type | Description |
|---|---|---|
Id | string | Unique license identifier (e.g., "IS-20260103-A1B2C3") |
Organization | string | Organization name |
Email | string | Contact email address |
Developers | int | Number of developers licensed |
Issued | DateTime | License issue date |
IsTrial | bool | True if this is a trial license |
Limit | int | Transaction limit for trial licenses (0 for full licenses) |
Products | IReadOnlyList<LicensedProductInfo> | All licensed products with expiration dates |
Computed Properties
| Property | Type | Description |
|---|---|---|
Expires | DateTime | Earliest expiration date across all products |
DaysRemaining | int | Days until earliest product expiration |
IsExpired | bool | True if all products have expired |
HasExpiredProducts | bool | True if any product has expired |
IsExpiringSoon | bool | True if any product expires within 30 days |
Status | string | Status description: "Active", "Trial", "Expired", etc. |
ActiveProducts | IEnumerable<...> | Products that are currently active (not expired) |
ExpiredProducts | IEnumerable<...> | Products that have expired |
ExpiringProducts | IEnumerable<...> | Products expiring within 30 days |
Methods
| Method | Returns | Description |
|---|---|---|
HasProduct(productCode) | bool | Check if a product is licensed and active |
ContainsProduct(productCode) | bool | Check if a product is in the license (regardless of expiration) |
GetProduct(productCode) | LicensedProductInfo? | Get info about a specific product |
LicensedProductInfo
Information about a licensed product. Each license can contain multiple products.
| Property | Type | Description |
|---|---|---|
ProductCode | string | Product code (e.g., "INTEROPSUITE-CLINICAL") |
ProductName | string | Display name (e.g., "InteropSuite Clinical") |
Expires | DateTime | Product expiration date |
DaysRemaining | int | Days until this product expires |
IsExpired | bool | True if this product has expired |
IsExpiringSoon | bool | True if this product expires within 30 days |
Status | string | "Active", "Expiring Soon", or "Expired" |
Product Codes
| Product Code | Display Name | Enables |
|---|---|---|
INTEROPSUITE-CLINICAL | InteropSuite Clinical | HL7 v2.x transformation |
INTEROPSUITE-CLAIMS | InteropSuite Claims | X12 5010 to US Core |
INTEROPSUITE-CLAIMS-CMS | InteropSuite Claims CMS-0057-F | X12 5010 to CMS-0057-F (Da Vinci, CARIN) |
INTEROPSUITE-CDA | InteropSuite CDA | C-CDA R2.1 transformation |
License Inspection Example
var info = Interop.GetLicenseInfo();
if (info == null)
{
Console.WriteLine("No license activated");
return;
}
// Basic info
Console.WriteLine($"License: {info.Id}");
Console.WriteLine($"Organization: {info.Organization}");
Console.WriteLine($"Status: {info.Status}");
Console.WriteLine($"Days remaining: {info.DaysRemaining}");
// List all products
foreach (var product in info.Products)
{
Console.WriteLine($" [{product.Status}] {product.ProductName}");
Console.WriteLine($" Expires: {product.Expires:MMM d, yyyy} ({product.DaysRemaining} days)");
}
// Check for specific capability
if (info.HasProduct("INTEROPSUITE-CDA"))
{
Console.WriteLine("C-CDA transformation is available");
}
// Warn about expiring products
if (info.IsExpiringSoon)
{
foreach (var p in info.ExpiringProducts)
Console.WriteLine($"⚠ {p.ProductName} expires in {p.DaysRemaining} days");
}
Environment Variables
InteropSuite supports configuration via environment variables for containerized and cloud deployments.
License Configuration
| Variable | Description |
|---|---|
INTEROPSUITE_LICENSE |
Base64-encoded license key. Used by ActivateLicenseAuto() for automatic activation. |
Path Configuration
| Variable | Default | Description |
|---|---|---|
INTEROPSUITE_ROOT |
{workingDir}/interopsuite |
Root directory for all InteropSuite data. All other paths are relative to this unless overridden. |
INTEROPSUITE_DASHBOARD_DIR |
{root}/dashboard |
Directory for dashboard session data used by TraceServer. |
INTEROPSUITE_AUDIT_DIR |
{root}/audit |
Directory for audit log files. |
INTEROPSUITE_QUARANTINE_DIR |
{root}/quarantine |
Directory for quarantined messages (AES-256-GCM encrypted). |
INTEROPSUITE_OUTPUT_DIR |
{root}/output/fhir |
Directory for FHIR bundle output from batch processing. |
Priority Order
Configuration is resolved in this order (highest priority first):
Interop.SetDataRoot()- Code takes highest priority- Environment variables -
INTEROPSUITE_* - Defaults -
{workingDir}/interopsuite
Error Handling
The API uses ConversionResult to report errors rather than throwing exceptions.
Error Types
| Error Type | Description |
|---|---|
| Validation Errors | Input message failed preflight validation |
| Parsing Errors | Message could not be parsed (malformed) |
| Mapping Errors | Required fields missing for FHIR conversion |
| License Errors | Invalid or expired license |
Recommended Pattern
var result = await Interop.HL7ToFhirAsync(message);
if (!result.Success)
{
// Log errors
foreach (var error in result.Errors)
_logger.LogError("Transformation error: {Error}", error);
// Handle failure (e.g., quarantine the message)
return BadRequest(result.Errors);
}
// Log warnings but continue
foreach (var warning in result.Warnings)
_logger.LogWarning("Transformation warning: {Warning}", warning);
// Process the bundle
return Ok(result.FhirBundle);
License Exceptions
License-related issues are returned in the result, not thrown as exceptions:
var result = await Interop.HL7ToFhirAsync(message);
if (result.Errors.Any(e => e.Contains("license")))
{
// Handle license issue
Console.WriteLine("License required or expired");
}
Complete Examples
Full working examples demonstrating common integration patterns.
Console Application
A complete console app for batch processing with progress reporting:
using System;
using System.Threading;
using System.Threading.Tasks;
using InteropSuite.Fhir.Engine;
using InteropSuite.Fhir.Engine.Batch;
class Program
{
static async Task Main(string[] args)
{
// 1. Configure paths (optional)
Interop.SetDataRoot("/var/interopsuite");
// 2. Enable dashboard
Interop.DashboardEnabled = true;
// 3. Activate license
Interop.ActivateLicenseAuto();
// 4. Show license info
var license = Interop.GetLicenseInfo();
Console.WriteLine($"Licensed to: {license?.Organization}");
Console.WriteLine($"Status: {license?.Status}");
Console.WriteLine();
// 5. Set up cancellation for Ctrl+C
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
Console.WriteLine("\nCancellation requested...");
};
// 6. Configure batch options
var options = new BatchOptions
{
Sources = new[]
{
new BatchSource("./input/hl7", "HL7", OutputFormat.UsCore),
new BatchSource("./input/cda", "CDA", OutputFormat.UsCore),
new BatchSource("./input/x12", "X12", OutputFormat.Cms0057F),
},
Workers = 4,
HeavyWorkers = 2,
EnableCheckpoints = true
};
// 7. Set up progress reporter
var progress = new Progress<BatchProgress>(p =>
{
Console.Write($"\r[{p.PercentComplete,5:F1}%] {p.ProcessedFiles}/{p.TotalFiles} files");
});
// 8. Run batch processing
Console.WriteLine("Starting batch processing...");
var result = await Interop.ProcessBatchAsync(options, progress, cts.Token);
// 9. Print results
Console.WriteLine();
Console.WriteLine();
Console.WriteLine($"═══════════════════════════════════════");
Console.WriteLine($" Results");
Console.WriteLine($"═══════════════════════════════════════");
Console.WriteLine($" Total files: {result.TotalFiles}");
Console.WriteLine($" Succeeded: {result.Succeeded}");
Console.WriteLine($" Failed: {result.Failed}");
Console.WriteLine($" FHIR resources: {result.TotalResources:N0}");
Console.WriteLine($" Duration: {result.Duration}");
Console.WriteLine($" Throughput: {result.FilesPerSecond:F1} files/sec");
Console.WriteLine($" Dashboard: {Interop.DashboardDirectory}");
}
}
ASP.NET Core Web API
A REST API controller for healthcare message transformation:
using Microsoft.AspNetCore.Mvc;
using InteropSuite.Fhir.Engine;
[ApiController]
[Route("api/[controller]")]
public class TransformController : ControllerBase
{
/// <summary>
/// Transform any healthcare message to FHIR.
/// Auto-detects HL7 v2.x, X12, or C-CDA format.
/// </summary>
[HttpPost]
[Consumes("text/plain")]
[Produces("application/fhir+json")]
public async Task<IActionResult> Transform(
[FromBody] string message,
[FromQuery] string? format = null,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(message))
return BadRequest("Message body is required");
// Determine output format
var output = format?.ToLowerInvariant() == "cms0057f"
? OutputFormat.Cms0057F
: OutputFormat.UsCore;
// Transform - cancellation token handles client disconnect
var result = await Interop.ToFhirAsync(message, output, cancellationToken);
if (!result.Success)
{
return BadRequest(new
{
success = false,
detectedFormat = result.DetectedFormat,
errors = result.Errors
});
}
// Return FHIR bundle with metadata headers
Response.Headers["X-Detected-Format"] = result.DetectedFormat;
Response.Headers["X-Resource-Count"] = result.ResourceCount.ToString();
return Content(result.FhirBundle!, "application/fhir+json");
}
/// <summary>
/// Get license status.
/// </summary>
[HttpGet("license")]
public IActionResult GetLicense()
{
var info = Interop.GetLicenseInfo();
if (info == null)
return Ok(new { activated = false });
return Ok(new
{
activated = true,
organization = info.Organization,
status = info.Status,
daysRemaining = info.DaysRemaining,
products = info.Products.Select(p => new
{
name = p.ProductName,
status = p.Status,
daysRemaining = p.DaysRemaining
})
});
}
}
Program.cs Setup (ASP.NET Core)
using InteropSuite.Fhir.Engine;
var builder = WebApplication.CreateBuilder(args);
// Configure InteropSuite at startup
Interop.DashboardEnabled = true;
Interop.ActivateLicenseAuto(); // Uses INTEROPSUITE_LICENSE env var
// Log license status
var license = Interop.GetLicenseInfo();
if (license != null)
{
builder.Services.AddSingleton(license);
Console.WriteLine($"InteropSuite licensed to {license.Organization}");
}
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Azure Function
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using InteropSuite.Fhir.Engine;
public class TransformFunction
{
[Function("Transform")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
// Read message body
using var reader = new StreamReader(req.Body);
var message = await reader.ReadToEndAsync();
// Transform
var result = await Interop.ToFhirAsync(message);
var response = req.CreateResponse(
result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "application/fhir+json");
if (result.Success)
await response.WriteStringAsync(result.FhirBundle!);
else
await response.WriteAsJsonAsync(new { errors = result.Errors });
return response;
}
}