Documentation

MedHook Documentation

A self-hosted healthcare data integration platform. Deploy the engine via Docker, build DAG-based workflows visually, connect EHRs and labs through 12 typed adapters, and integrate with Claude through MCP.

Quick Start

Get a local engine running in three steps. Total time: under five minutes once Docker is installed.

1

Get your license key

Sign up on medhook.dev, open Dashboard → API Keys, and click Create Key. Keys look like mdh_live_… or mdh_test_…. They are shown only once — copy immediately.

2

Configure environment

Clone the engine repo, copy the example env file, and fill in five required variables.

bash
git clone https://github.com/medhook/engine.git
cd engine
cp .env.example .env
VariableSource / Value
DATABASE_URLPostgreSQL connection string
REDIS_URLRedis connection string
ENCRYPTION_MASTER_KEYopenssl rand -hex 32
ENGINE_AUTH_SECRETopenssl rand -hex 64
LICENSE_KEYmedhook.dev → Dashboard → API Keys
3

Start the engine

Bring up the 8-service stack (engine, Redis, Postgres, Medplum, MLLP, Prometheus, Grafana, optional VPN), then verify health.

bash
docker compose up -d

# verify
curl http://localhost:3000/api/health
# → { "status": "healthy" }

# open the dashboard
open http://localhost:3000

Core Concepts

Every integration in MedHook is a directed acyclic graph (DAG): a trigger fires, nodes execute, and edges connect them. Branching, parallel execution, and loops are all expressed as nodes — there is no separate orchestration layer.

triggernodenoderesult

Node types (7)

Node typeDescription
adapterCall an integration adapter (read / write).
transformApply field mappings, code tables, or custom JS.
conditionIf / else branching based on data expressions.
parallelExecute multiple branches concurrently.
loopIterate over arrays with a max-iteration guard.
waitWait for a webhook callback or timer.
triggerStart another workflow as a sub-process.

Trigger types (8)

Trigger typeDescription
manualUser-initiated execution from UI or API.
webhookInbound HTTP POST with optional HMAC validation.
scheduleSimple cron expression scheduling.
cronAdvanced cron with timezone support.
pollingInterval-based adapter polling.
mllpHL7v2 message via MLLP TCP listener.
fhir-subscriptionFHIR R4 subscription notifications.
x12X12 EDI transaction ingestion (SFTP / webhook / upload).

Transform types (8)

Transform typeDescription
field-mappingVisual drag-and-drop field mapper.
javascriptCustom sandboxed JS (isolated-vm, 128 MB, 5 s timeout).
code-tableLookup / translation tables.
hl7v2-to-fhirBuilt-in HL7v2 → FHIR R4 conversion.
fhir-to-hl7v2Built-in FHIR R4 → HL7v2 conversion.
x12-parseParse X12 EDI to JSON.
x12-generateGenerate X12 EDI from JSON.
templateTemplate-based rendering.

Adapters

Adapters are typed connectors — config validated by Zod, credentials encrypted with AES-256-GCM, connection-tested before use. Twelve adapters ship in the box.

FHIR REST

fhir-rest
Auth
Basic, Bearer, OAuth2
Use case
Any FHIR R4 server

Epic FHIR

epic-fhir
Auth
JWT Backend Services
Use case
Epic EHR integration

Athena FHIR

athena-fhir
Auth
Bearer token
Use case
athenahealth integration

Oracle FHIR

oracle-fhir
Auth
Basic, Bearer
Use case
Oracle Health / Cerner

Medplum

medplum
Auth
Client credentials
Use case
Local FHIR reference server

Generic REST

generic-rest
Auth
Basic, Bearer, API Key
Use case
Any REST API (SSRF-protected)

SFTP

sftp
Auth
Password, Private Key
Use case
File-based integrations

Webhook

webhook
Auth
Header, HMAC
Use case
Inbound webhook receiver

MLLP Outbound

mllp
Auth
N/A (TCP)
Use case
HL7v2 over TCP

X12 EDI

x12
Auth
N/A
Use case
837 / 835 / 270 / 271 transactions

FHIR Bulk

fhir-bulk
Auth
Bearer, OAuth2
Use case
Bulk data export / import

Base (abstract)

base
Auth
Use case
Extension interface for custom adapters

Adding a custom adapter

  1. Create engine/src/adapters/my-adapter/index.ts.
  2. Extend BaseAdapter and define type, configSchema (Zod), read(), and write().
  3. Register the class in engine/src/lib/init-adapters.ts.
  4. The adapter automatically appears in the workflow designer's sidebar.
engine/src/adapters/my-adapter/index.ts
import { z } from 'zod';
import { BaseAdapter } from '../base/adapter';

export class MyAdapter extends BaseAdapter {
  readonly type = 'my-adapter';
  readonly name = 'My Adapter';
  readonly description = 'Connects to my system';
  readonly version = '1.0.0';

  readonly configSchema = z.object({
    baseUrl: z.string().url(),
    apiKey: z.string().min(1),
  });

  readonly actions = [
    { id: 'read', label: 'Read records' },
    { id: 'write', label: 'Write records' },
  ];

  async read(profile, params) { /* ... */ }
  async write(profile, data)  { /* ... */ }
}

API Reference

Every action in the engine is reachable through a REST API. JWT bearer tokens are obtained from /api/auth/token and expire after 2 hours. The full reference lives at engine/docs/API.md.

Authentication

MethodEndpoint
POST/api/auth/token
auth
# Exchange your engine auth secret for a JWT
TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"secret":"'"$ENGINE_AUTH_SECRET"'"}' | jq -r .token)

# Use it
curl http://localhost:3000/api/workflows \
  -H "Authorization: Bearer $TOKEN"

Workflows

MethodEndpoint
GET/api/workflows
POST/api/workflows
GET/api/workflows/:id
PUT/api/workflows/:id
DELETE/api/workflows/:id
GET/api/workflows/templates
create workflow
curl -X POST http://localhost:3000/api/workflows \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "EHR → Lab Sync",
    "trigger": { "type": "schedule", "cron": "0 */6 * * *" },
    "nodes": [ /* DAG nodes */ ],
    "edges": [ /* DAG edges */ ]
  }'

Execution

MethodEndpoint
POST/api/workflows/:id/execute
GET/api/executions
GET/api/executions/:id
GET/api/executions/:id/stream
POST/api/executions/:id/cancel

Adapter profiles

MethodEndpoint
GET/api/adapter-profiles
POST/api/adapter-profiles
PUT/api/adapter-profiles/:id
POST/api/adapter-profiles/:id/test

Looking for the complete catalog (mapping sets, code tables, triggers, GraphQL, metrics)? See engine/docs/API.md.

Deployment

MedHook is fully self-hosted. The fastest path is Docker Compose for local and single-server deployments; Terraform templates exist for AWS, Azure, and GCP.

Docker Compose

Spin up the entire 8-service stack — engine, Postgres, Redis, Medplum, MLLP listener, Prometheus, Grafana, optional IPSec VPN — with one command.

bash
# from the engine repo root
docker compose up -d

# tail logs
docker compose logs -f engine

# stop
docker compose down

Cloud (AWS / Azure / GCP)

Each cloud has a self-contained Terraform module that provisions networking, secrets, the container service, and a managed Postgres + Redis. Pick the closest match to your existing infrastructure.

AWS Fargate
engine/terraform/aws
Azure Container Instances
engine/terraform/azure
GCP Cloud Run
engine/terraform/gcp
bash — AWS example
cd engine/terraform/aws
terraform init
terraform apply

On-prem / air-gapped: any Docker-capable host works. For offline license activation and private image registries, contact us.

MCP Integration

The engine exposes a Model Context Protocol stdio server so Claude Desktop (and other MCP clients) can drive your workflows directly. Twelve tools cover the workflow lifecycle, adapter management, and clinical-format parsing.

Add to Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add the server:

claude_desktop_config.json
{
  "mcpServers": {
    "medhook": {
      "command": "node",
      "args": ["/absolute/path/to/engine/dist/mcp/server.js"],
      "env": {
        "MEDHOOK_ENGINE_URL": "http://localhost:3000",
        "MEDHOOK_AUTH_SECRET": "your-engine-auth-secret"
      }
    }
  }
}

Available tools (12)

list-workflowsget-workflowcreate-workflowexecute-workflowget-executionlist-adapterslist-adapter-profilestest-adapter-profilesuggest-mappingparse-hl7v2parse-x12list-code-tables

Full tool schemas, resource URIs, and example prompts live at engine/docs/mcp.md.

Security

Healthcare-grade defaults. PHI never leaves your infrastructure: medhook.dev only receives HMAC-signed usage analytics — never workflow data.

AES-256-GCM at rest

All adapter credentials are encrypted with AES-256-GCM, PBKDF2 key derivation (100k iterations), and unique salt + IV per record. Master key from ENCRYPTION_MASTER_KEY.

JWT auth (2 h expiry)

Bearer tokens signed with ENGINE_AUTH_SECRET, 2-hour expiry, validated by middleware on every protected route.

Sandboxed JS execution

Custom JavaScript transforms run inside isolated-vm with a 128 MB memory cap and 5 s timeout. Inputs are read-only frozen objects.

Audit logging

Every CRUD event on workflows, adapter profiles, triggers, mappings, and licenses is written to Postgres with 90-day default retention.

SSRF protection

The Generic REST adapter validates URLs against an allowlist / denylist and blocks traffic to private IP ranges and link-local addresses by default.

RBAC

User and organization-level permissions gate workflow editing, adapter profile access, and license management. Admin-only routes are enforced at the middleware layer.

Disclosure: see /security for our vulnerability reporting policy and the latest audit summary.

Ready to ship?

Get a license key and run docker compose up -d. Your first integration can be live in minutes.