Quick Start
Get a local engine running in three steps. Total time: under five minutes once Docker is installed.
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.
Configure environment
Clone the engine repo, copy the example env file, and fill in five required variables.
git clone https://github.com/medhook/engine.git
cd engine
cp .env.example .env| Variable | Source / Value |
|---|---|
| DATABASE_URL | PostgreSQL connection string |
| REDIS_URL | Redis connection string |
| ENCRYPTION_MASTER_KEY | openssl rand -hex 32 |
| ENGINE_AUTH_SECRET | openssl rand -hex 64 |
| LICENSE_KEY | medhook.dev → Dashboard → API Keys |
Start the engine
Bring up the 8-service stack (engine, Redis, Postgres, Medplum, MLLP, Prometheus, Grafana, optional VPN), then verify health.
docker compose up -d
# verify
curl http://localhost:3000/api/health
# → { "status": "healthy" }
# open the dashboard
open http://localhost:3000Core 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.
Node types (7)
| Node type | Description |
|---|---|
| adapter | Call an integration adapter (read / write). |
| transform | Apply field mappings, code tables, or custom JS. |
| condition | If / else branching based on data expressions. |
| parallel | Execute multiple branches concurrently. |
| loop | Iterate over arrays with a max-iteration guard. |
| wait | Wait for a webhook callback or timer. |
| trigger | Start another workflow as a sub-process. |
Trigger types (8)
| Trigger type | Description |
|---|---|
| manual | User-initiated execution from UI or API. |
| webhook | Inbound HTTP POST with optional HMAC validation. |
| schedule | Simple cron expression scheduling. |
| cron | Advanced cron with timezone support. |
| polling | Interval-based adapter polling. |
| mllp | HL7v2 message via MLLP TCP listener. |
| fhir-subscription | FHIR R4 subscription notifications. |
| x12 | X12 EDI transaction ingestion (SFTP / webhook / upload). |
Transform types (8)
| Transform type | Description |
|---|---|
| field-mapping | Visual drag-and-drop field mapper. |
| javascript | Custom sandboxed JS (isolated-vm, 128 MB, 5 s timeout). |
| code-table | Lookup / translation tables. |
| hl7v2-to-fhir | Built-in HL7v2 → FHIR R4 conversion. |
| fhir-to-hl7v2 | Built-in FHIR R4 → HL7v2 conversion. |
| x12-parse | Parse X12 EDI to JSON. |
| x12-generate | Generate X12 EDI from JSON. |
| template | Template-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-restEpic FHIR
epic-fhirAthena FHIR
athena-fhirOracle FHIR
oracle-fhirMedplum
medplumGeneric REST
generic-restSFTP
sftpWebhook
webhookMLLP Outbound
mllpX12 EDI
x12FHIR Bulk
fhir-bulkBase (abstract)
baseAdding a custom adapter
- Create
engine/src/adapters/my-adapter/index.ts. - Extend
BaseAdapterand definetype,configSchema(Zod),read(), andwrite(). - Register the class in
engine/src/lib/init-adapters.ts. - The adapter automatically appears in the workflow designer's sidebar.
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
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/token | Exchange engine auth secret for a JWT (2 h expiry) |
# 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
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/workflows | List workflows |
| POST | /api/workflows | Create a workflow |
| GET | /api/workflows/:id | Get a workflow |
| PUT | /api/workflows/:id | Update a workflow |
| DELETE | /api/workflows/:id | Delete a workflow |
| GET | /api/workflows/templates | List built-in workflow templates |
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
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/workflows/:id/execute | Execute a workflow |
| GET | /api/executions | List executions |
| GET | /api/executions/:id | Get execution status |
| GET | /api/executions/:id/stream | Server-sent events stream of node results |
| POST | /api/executions/:id/cancel | Cancel a running execution |
Adapter profiles
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/adapter-profiles | List adapter profiles |
| POST | /api/adapter-profiles | Create an encrypted adapter profile |
| PUT | /api/adapter-profiles/:id | Update a profile |
| POST | /api/adapter-profiles/:id/test | Test connection |
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.
# from the engine repo root
docker compose up -d
# tail logs
docker compose logs -f engine
# stop
docker compose downCloud (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.
engine/terraform/awsengine/terraform/azureengine/terraform/gcpcd engine/terraform/aws
terraform init
terraform applyOn-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:
{
"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-tablesFull 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
ENCRYPTION_MASTER_KEY.JWT auth (2 h expiry)
ENGINE_AUTH_SECRET, 2-hour expiry, validated by middleware on every protected route.Sandboxed JS execution
Audit logging
SSRF protection
RBAC
Disclosure: see /security for our vulnerability reporting policy and the latest audit summary.