Skip to content

ApprovalML

ApprovalML is the open workflow definition language underneath Aptiwise. Every workflow — whether AI-generated or hand-authored — compiles to ApprovalML YAML: a portable, version-controllable format for approval routing, forms, and automation logic. The full language (forms, field types, roles, data sources) is what the hosted Aptiwise product runs. A self-hostable, open-source runtime also exists for a deliberately smaller slice of that language.

Open & Self-Hostable

Run a lightweight subset of the approval engine on your own infrastructure with Docker — no external cloud dependency required.

Gated MCP

Put human-in-the-loop approval gates in front of autonomous AI agents. An agent requests approval over MCP; a human approves or rejects by email, no login required.

Wrap Any MCP Server

approvalml -- npx -y @modelcontextprotocol/server-github spawns an existing stdio MCP server as a child process and re-exposes it with sensitive tools gated behind human approval — no changes to the upstream server required.

Declarative YAML

Steps, routing, and SLAs are defined as data, not code — reviewable in a pull request like any other config.

REST API

The full workflow lifecycle — submit, approve, reject, track — is available over REST, in addition to the MCP server.

The open-source runtime supports the step types below. It does not render dynamic forms or resolve roles through an org directory (only flat env-var role mapping — see below) — treat it as a headless approval gate, not a forms/workflow-designer product.

Step type Supported Notes
decision (human approval) Core feature — email link, no-login approve/reject
notification Requires SMTP config or prints to stdout
conditional_split Condition evaluation runs fully inline
field_mapping Inline data transformation, no external calls
asset Reads/writes named JSON records in PostgreSQL
parallel_approval Multiple approvers, threshold logic
spawn Fan-out to child workflow instances
data_processor / data_source ⚠️ Requires a connector to be pre-seeded directly in the database — no UI

The open-source runtime is intentionally headless and scoped to individual/personal use as a gated MCP approval server. It does not support:

  • Dynamic form rendering — the Field Types, Form Layouts, and Multi-page Forms syntax is part of the Aptiwise product surface, not the self-hosted runtime
  • Org-directory role routing — no Role Fields, department/skill-based assignment, or org hierarchy; a bare approver: role_name resolves only through a flat APPROVALML_ROLE_<NAME> environment variable, not a directory lookup
  • Data source / connector UIdata_processor and data_source steps require a connector row inserted manually into the database; there is no admin screen to configure one
  • Multi-tenancy, admin UI, workflow designer UI, or reporting/analytics

The runtime also works as a gate in front of an existing MCP server, not just as a standalone approval engine — useful for putting a human checkpoint in front of tools an AI agent already has access to (e.g. a GitHub or filesystem MCP server) without modifying that server:

{
"github-guarded": {
"command": "uvx",
"args": ["approvalml", "--", "npx", "-y", "@modelcontextprotocol/server-github"],
"env": {
"APPROVALML_APPROVER": "you@personal.com",
"APPROVALML_NOTIFY": "slack:https://hooks.slack.com/services/XXX"
}
}
}

approvalml -- <command> <args> spawns the upstream MCP server as a child process and re-exposes its tools, classifying each one as:

  • auto — read-only tools (get_/list_/search_/read_/describe_ naming, or an MCP readOnlyHint annotation) pass straight through
  • gate — sensitive tools (create_/update_/delete_/send_/execute_ naming, or a destructiveHint annotation) pend for a human approval before the real call is forwarded
  • workflow — routed through a full named ApprovalML workflow instead of a single-step gate, for multi-step or conditional approval
  • deny — never exposed to the calling agent

Zero-config classification only auto-passes a tool when the naming heuristic and the tool’s MCP annotations agree; any disagreement or missing signal defaults to gate rather than silently allowing or blocking the call. An APPROVALML_CONFIG YAML file can override this with explicit glob rules for tighter control — either a flat rules:/default_action list routing individual tools to separately named workflows, or a guards.tools map where one file is both the classifier and the escalation workflow for the whole server (see MCP Tool Guards in the syntax reference).

APPROVALML_NOTIFY=<channel>:<target> (for example slack:https://hooks.slack.com/...) sends a notification to Slack or another webhook-based channel alongside — not instead of — the email approval link.

Terminal window
# 1. Clone and configure
git clone https://github.com/dickyj-grc/ApprovalML.git
cd ApprovalML
cp deployment/docker-compose.standalone.yml docker-compose.yml
cp deployment/.env.example .env
# Edit .env: set SMTP_HOST, ADMIN_EMAIL, SECRET_KEY
# 2. Start
docker compose up -d
# 3. Load your workflow YAMLs
approvalml load ./workflows/vendor-invoice.yaml

Hosted SaaS (Aptiwise) vs. open-source runtime

Section titled “Hosted SaaS (Aptiwise) vs. open-source runtime”
Feature Open-source runtime Aptiwise (hosted SaaS)
Approval engine (decision, parallel_approval, spawn, etc.)
Email approvals (no login)
MCP server / gated MCP
Wrap an existing stdio MCP server (approvalml -- <cmd>) ✅ Same CLI, pointed at Aptiwise via APPROVALML_API_URL
Dynamic form rendering (field types, layouts)
Role-based approval routing ⚠️ Flat env-var mapping only ✅ Org directory & hierarchy
Data source / connector UI ❌ Manual DB seeding only
AI workflow generation
Admin & workflow designer UI ❌ Not included
Multi-tenant (multiple companies) ❌ Single company
SLA escalation ✅ (via cron tick endpoint)
Reporting / analytics