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.

Thread Safety

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
)
ParameterTypeDescription
payloadstringRaw HL7 v2.x message content
progressIProgress<TransformProgress>?Optional progress reporter for real-time status updates
cancellationTokenCancellationTokenOptional 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
)
ParameterTypeDescription
payloadstringRaw X12 EDI content
outputOutputFormatUS Core (default) or CMS-0057-F profiles
progressIProgress<TransformProgress>?Optional progress reporter for real-time status updates
cancellationTokenCancellationTokenOptional 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
)
ParameterTypeDescription
payloadstringRaw C-CDA XML content
progressIProgress<TransformProgress>?Optional progress reporter. Useful for large CDA files with chunked processing.
cancellationTokenCancellationTokenOptional cancellation token

Returns: ConversionResult containing the FHIR bundle or errors.

Embedded Media Not Supported

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
)
ParameterTypeDescription
payloadstringRaw message content (HL7, X12, or C-CDA)
outputOutputFormatOutput profile (only affects X12)
progressIProgress<TransformProgress>?Optional progress reporter for real-time status updates
cancellationTokenCancellationTokenOptional 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

PropertyTypeDefaultDescription
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)

PropertyTypeDefault LocationDescription
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)
ParameterTypeDescription
pathstringAbsolute 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()
Best Practice

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);
}
Best Practice

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.

Dual-Queue Architecture

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:

ModeUse CaseBehavior
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
)
ParameterTypeDescription
inputDirectorystringPath to directory containing input files
optionsBatchOptionsBatch processing configuration
cancellationTokenCancellationTokenOptional 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
Each batch completes before the next starts. Creates separate TraceServer sessions.

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}");
Each source can have its own 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);
}
PropertyTypeDescription
InputDirectorystringPath to input files for this source
DomainstringDomain identifier for TraceServer (e.g., "HL7", "X12", "CDA")
OutputFormatOutputFormatUsCore or Cms0057F for this source

Sequential vs Unified Comparison

AspectSequentialUnified (RoundRobin)
APIProcessBatchAsync(string, BatchOptions)ProcessBatchAsync(BatchOptions) with Sources
ProcessingOne domain at a timeInterleaved across all domains
BlockingDomain A blocks Domain BNo blocking between domains
TraceServerSeparate sessions per batchSingle unified session
OutputFormatGlobal per batchPer-source configurable
Unified Mode is Recommended

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.

PropertyTypeDefaultDescription
SourcesBatchSource[]?nullMulti-source configuration for unified mode
Domainstring?nullFilter: "HL7", "X12", "CDA", or null for auto-detect (single-source mode)
OutputFormatOutputFormatUsCoreOutput profile (UsCore or Cms0057F) for single-source mode
Workersint?autoParallel workers for small files (auto = CPU cores / 2, min 2, max 8)
HeavyWorkersint?autoWorkers for large files (auto = Workers / 2, min 1, max 4)
LargeFileSizeThresholdlong1MBFiles above this size use heavy queue with memory throttling
MaxFileSizeByteslong50MBMaximum file size allowed (50MB hard cap). Files exceeding this limit are skipped. Values above 50MB are automatically capped.
EnableCheckpointsboolfalseEnable pause/resume for very large batches
ContinueOnErrorbooltrueContinue processing if individual files fail
FileExtensionsstring[].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 SizeFilesPass RateAvg DurationResources
< 1 KB3381.8%1,408 ms140
1 - 10 KB1485.7%223 ms262
10 - 100 KB2100%148 ms4

X12 5010 Performance

192 files processed (US Core compliance).

File SizeFilesPass RateAvg DurationResources
< 1 KB164100%271 ms734
1 - 10 KB2892.9%115 ms283

X12 CMS-0057-F Performance

563 files processed (Da Vinci PAS / CARIN Blue Button compliance).

File SizeFilesPass RateAvg DurationResources
< 1 KB563100%160 ms4,074

C-CDA R2.1 Performance

692 files processed using chunked memory-safe processing for large files.

File SizeFilesPass RateAvg DurationResources
1 - 10 KB12100%7.8 sec0
10 - 100 KB123100%18.9 sec2,959
100 KB - 1 MB45899.8%31.4 sec223,077
1 - 10 MB9498.9%1.3 min143,069
10 - 50 MB580%27.3 min28,619

Largest C-CDA Files Processed

File SizeDurationResourcesStatus
45.0 MB27.9 min10,416✔ Passed
18.1 MB16.5 min8,291✔ Passed
13.5 MB15.5 min5,406✔ Passed
11.6 MB7.4 min4,506✔ Passed
9.1 MB6.3 min3,959✔ Passed
Tested with Large Files

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.

PropertyTypeDescription
TotalFilesintTotal number of files processed
SucceededintNumber of files successfully transformed
FailedintNumber of files that failed
SkippedintNumber of files skipped due to exceeding MaxFileSizeBytes limit
TotalResourcesintTotal FHIR resources created
DurationTimeSpanTotal processing time
Output Structure

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.

Memory Considerations

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.

PropertyTypeDescription
FilePathstringFull path to the source file
FileNamestringFile name only (without path)
SuccessboolWhether the file was processed successfully
MessageTypestring?Detected message type (e.g., "X12 837P", "HL7 ADT^A01", "CDA 34133-9")
ResourceCountintNumber of FHIR resources generated
DurationTimeSpanProcessing time for this file
ErrorMessagestring?Error message if processing failed
WasQuarantinedboolWhether the file was quarantined (invalid/malformed)
OutputPathstring?Relative path to output FHIR bundle file
SourceSizelongSource file size in bytes
ValidationErrorCountintNumber of FHIR validation errors. Zero = validated/ folder, >0 = errors/ folder

BatchResult Additional Properties

Computed properties available on BatchResult for statistics:

PropertyTypeDescription
QuarantinedintNumber of files that were quarantined
AverageMillisecondsPerFiledoubleAverage processing time per file
FilesPerSeconddoubleProcessing throughput
SuccessRatedoubleSuccess percentage (0-100)
InputDirectorystring?The input directory that was processed
OutputDirectorystring?The output directory where bundles were written

BatchProgress

Progress information passed to IProgress<BatchProgress> callback during batch processing.

PropertyTypeDescription
TotalFilesintTotal number of files to process
ProcessedFilesintNumber of files processed so far
SucceededFilesintNumber of successful files so far
FailedFilesintNumber of failed files so far
CurrentFilestring?Name of the file currently being processed
PercentCompletedoubleProgress percentage (0-100)

TransformProgress

Progress information passed to IProgress<TransformProgress> callback during single-file transformations. Useful for large CDA documents that use chunked processing.

PropertyTypeDescription
PhaseTransformPhaseCurrent processing phase
MessagestringHuman-readable message describing current activity
PercentCompletedoubleProgress percentage (0-100)
CurrentChunkintFor chunked CDA: current chunk number (1-based)
TotalChunksintFor chunked CDA: total number of chunks
TotalEntriesintFor chunked CDA: total clinical entries in the document
ProcessedEntriesintFor chunked CDA: entries processed so far
ResourcesCreatedintNumber of FHIR resources created so far
ElapsedTimeSpanElapsed time since processing started

TransformPhase Enum

Processing phases reported by TransformProgress:

ValueDescription
DetectingDetecting input format (HL7, X12, CDA)
ParsingParsing input message
ValidatingValidating input against rules
TransformingTransforming to canonical model
BuildingFhirBuilding FHIR resources
ProcessingChunkProcessing chunk (for large CDA documents)
MergingChunksMerging chunks (for large CDA documents)
ValidatingFhirValidating FHIR output
CompleteProcessing 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 ModeSession TrackingTraceServer 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
Real-Time Updates

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

PropertyTypeDescription
SuccessboolTrue if transformation completed successfully
FhirBundlestring?JSON-serialized FHIR Bundle (null if failed)
DetectedFormatstring?Detected message type (e.g., "ADT^A01", "X12^837P")
OutputModeOutputFormatProfile mode used (UsCore or Cms0057F)
ErrorsList<string>List of error messages (blocking issues)
WarningsList<string>List of warning messages (non-blocking)
ResourceCountintNumber 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.

ValueDescriptionApplies To
UsCore US Core 6.1.0 profiles (default) All formats
Cms0057F CMS-0057-F compliant profiles (CARIN, Da Vinci PAS, PDex) X12 only
Note

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:

  1. INTEROPSUITE_LICENSE environment variable
  2. interopsuite.lic file in current directory
  3. interopsuite.lic file 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().

PropertyTypeDescription
IdstringUnique license identifier (e.g., "IS-20260103-A1B2C3")
OrganizationstringOrganization name
EmailstringContact email address
DevelopersintNumber of developers licensed
IssuedDateTimeLicense issue date
IsTrialboolTrue if this is a trial license
LimitintTransaction limit for trial licenses (0 for full licenses)
ProductsIReadOnlyList<LicensedProductInfo>All licensed products with expiration dates

Computed Properties

PropertyTypeDescription
ExpiresDateTimeEarliest expiration date across all products
DaysRemainingintDays until earliest product expiration
IsExpiredboolTrue if all products have expired
HasExpiredProductsboolTrue if any product has expired
IsExpiringSoonboolTrue if any product expires within 30 days
StatusstringStatus description: "Active", "Trial", "Expired", etc.
ActiveProductsIEnumerable<...>Products that are currently active (not expired)
ExpiredProductsIEnumerable<...>Products that have expired
ExpiringProductsIEnumerable<...>Products expiring within 30 days

Methods

MethodReturnsDescription
HasProduct(productCode)boolCheck if a product is licensed and active
ContainsProduct(productCode)boolCheck 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.

PropertyTypeDescription
ProductCodestringProduct code (e.g., "INTEROPSUITE-CLINICAL")
ProductNamestringDisplay name (e.g., "InteropSuite Clinical")
ExpiresDateTimeProduct expiration date
DaysRemainingintDays until this product expires
IsExpiredboolTrue if this product has expired
IsExpiringSoonboolTrue if this product expires within 30 days
Statusstring"Active", "Expiring Soon", or "Expired"

Product Codes

Product CodeDisplay NameEnables
INTEROPSUITE-CLINICALInteropSuite ClinicalHL7 v2.x transformation
INTEROPSUITE-CLAIMSInteropSuite ClaimsX12 5010 to US Core
INTEROPSUITE-CLAIMS-CMSInteropSuite Claims CMS-0057-FX12 5010 to CMS-0057-F (Da Vinci, CARIN)
INTEROPSUITE-CDAInteropSuite CDAC-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

VariableDescription
INTEROPSUITE_LICENSE Base64-encoded license key. Used by ActivateLicenseAuto() for automatic activation.

Path Configuration

VariableDefaultDescription
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):

  1. Interop.SetDataRoot() - Code takes highest priority
  2. Environment variables - INTEROPSUITE_*
  3. Defaults - {workingDir}/interopsuite

Error Handling

The API uses ConversionResult to report errors rather than throwing exceptions.

Error Types

Error TypeDescription
Validation ErrorsInput message failed preflight validation
Parsing ErrorsMessage could not be parsed (malformed)
Mapping ErrorsRequired fields missing for FHIR conversion
License ErrorsInvalid 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;
    }
}