Decision Tree DSL Reference — Syntax & Examples
This is the complete reference for the DrawDecisionTree DSL (Domain-Specific Language) — the plain-text .dag format used to define decision trees.
→ Try it in the editor · Jump to complete syntax reference ↓
Machine-Readable Format for AI and LLM Clients
The DSL described on this page is the human-authoring surface. The format that AI agents, MCP clients, and automated systems consume is the compiled DecisionDAG JSON document.
The authoritative JSON Schema for the DecisionDAG format is published at /decision-dag.schema.json. This schema is the stable contract for all programmatic consumers — use it for validation, type generation, and integration testing rather than parsing the DSL directly.
Every tree published to the DrawDecisionTree directory is accessible as a DecisionDAG JSON document via the API:
GET https://www.drawdecisiontree.com/api/t/{publisher}/{slug}
The DrawDecisionTree MCP server exposes published trees as resources at the URI scheme:
decision-tree://{publisher}/{slug}
MCP-compatible LLM clients — including Claude Desktop and any host implementing the Model Context Protocol — can discover and execute decision trees from the directory without a custom integration.
→ Full API and MCP reference · → How AI agents use decision trees in production · → Publish your tree to make it available via API
Basic Structure
A .dag file has four sections, in order:
- Metadata — top-level key/value fields describing the tree
- Questions & Answers — the nodes and edges of the tree
- Outcomes — definitions for every terminal result node
- Routes (optional) — explicit end-to-end path documentation
dag: Support Guide
version: 1.0.0
description: Diagnose common device issues.
entry: Q1
Q1: Is your device powered on?
hint: Check that the power cable is connected and the power light is on.
yes -> Q2
no -> [OUT_POWER]
Q2: Is it connected to the network?
yes -> [OUT_OK]
no -> [OUT_NETWORK]
[OUT_POWER]: Check power supply
description: Ensure the device is plugged in and the outlet is working.
code: FIX_POWER
[OUT_NETWORK]: Check network connection
description: Reconnect to Wi-Fi or reseat the ethernet cable.
code: FIX_NETWORK
[OUT_OK]: All systems nominal
description: No issue detected. If problems persist, restart the device.
code: NO_ISSUE
Metadata Fields
Metadata appears at the top of the file, one field per line. Fields are not indented.
| Field | Required | Description |
|---|---|---|
dag |
Yes | Display name of the decision tree |
version |
Yes | Version string for the tree, e.g. 1.0.0 (semver recommended) |
entry |
Yes | ID of the first question to show |
mode |
No | decision (default) or elimination |
description |
No | Short description shown in the header |
image |
No | https:// URL of a hero image shown above the tree |
tags |
No | Comma-separated list of topic tags, e.g. cloud, infrastructure |
dag: Cloud Provider Selection
version: 1.0.0
entry: Q1
mode: decision
description: Identify the best cloud platform for your workload.
image: https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1200&q=80
tags: cloud, infrastructure, devops
imagemust be a publicly accessiblehttps://URL. Non-http URLs are ignored and the default banner is shown instead.
Heads-up: mid-line,
#is reserved as a comment start unless it is immediately followed by 3, 6, or 8 hex digits (a hex colour). That means text likehint: check escalation path #4 or higheris parsed ashint: check escalation path— the#4 or higherportion is stripped. To include#followed by non-hex content in a value, rephrase to avoid the#(e.g.path number 4instead ofpath #4).
Questions
A question is any non-indented line in the form ID: Question text.
Q1: What type of data are you working with?
- IDs are alphanumeric and may include underscores (
A-Za-z0-9_), e.g.Q1,Q2,CHECK_DB - IDs must be unique across the file
- The question specified in
entry:is always shown first - Questions can appear in any order — the tree structure is determined by the answer links
Question Sub-fields
Optional indented lines can appear directly below a question:
| Sub-field | Description |
|---|---|
hint: <text> |
Contextual hint displayed below the question text |
when: <QID>=<answer> |
Conditional visibility — only show this question when the referenced question was answered with the given value (elimination mode only; one clause per question) |
Q3: Do you need multi-region writes?
hint: Active-active means writes are accepted in more than one region simultaneously.
when: Q2=A
A: Yes [AURORA, COSMOS]
B: No [POSTGRES, MYSQL, AURORA]
A question with when: is hidden until its condition is met. Once shown it behaves like any other question; it is not re-hidden if the referenced answer subsequently changes.
Answers
Answers are indented under their question with two spaces (or a tab). There are two answer styles.
Binary Answers (yes / no)
Use yes and no keywords when the question has exactly two options.
Q1: Is the service currently running?
yes -> Q2
no -> [OUT_START_SERVICE]
The -> arrow points to either another question ID or an outcome ID (wrapped in [ ]).
Choice Answers (A / B / C …)
Use letter-prefixed options (A–F) for multiple-choice questions.
Q1: What is your primary workload type?
A: Web application or API -> Q2
B: Data analytics or ML -> Q3
C: Enterprise / SAP / ERP -> [OUT_AZURE]
D: Edge or IoT -> [OUT_AWS]
- Letters must be uppercase:
A,B,C,D,E,F - The label (after the colon) is displayed to the user
- The
->arrow points to either a question ID or an outcome ID
Answer Link Syntax
Both binary and choice answers support these destination formats:
| Syntax | Meaning |
|---|---|
-> Q2 |
Navigate to question Q2 |
-> [OUT_NAME] |
Navigate to outcome OUT_NAME (brackets are optional) |
Outcome Definitions
Outcomes are defined at the end of the file in the form [ID]: Label. All outcome IDs referenced in answers must have a definition.
[OUT_AZURE]: Microsoft Azure
description: Best-in-class integration with Microsoft 365 and enterprise tooling.
code: CLOUD_AZURE
color: #0078D4
Outcome ID format: must be uppercase letters, digits, underscores, or hyphens (
A-Z0-9_-), e.g.[OUT_AZURE],[RESULT-1]. Lowercase is not allowed in outcome IDs (contrast with question IDs, which allow mixed case).
Outcome Properties
Indented properties can follow each outcome definition:
| Property | Description |
|---|---|
description: <text> |
Explanation shown on the result card |
code: <IDENTIFIER> |
Machine-readable identifier surfaced in the embed widget's selection event and in the API response — use this to act on outcomes programmatically |
color: <hex> |
Accent color for the result card (e.g. #FF9900) |
Decision Mode (Default)
In decision mode (mode: decision), each answer leads to exactly one destination. The tree is a directed acyclic graph (DAG) — no cycles allowed.
dag: API Design Advisor
version: 1.0.0
entry: Q1
Q1: Who are the primary consumers of this API?
A: Internal services -> Q2
B: Public / partners -> Q2
C: Mobile or browser -> Q3
Q2: Is low-latency real-time streaming required?
yes -> Q4
no -> Q5
Q3: Does the client need fine-grained field control?
yes -> [OUT_GRAPHQL]
no -> Q5
Q4: Is bidirectional communication needed?
yes -> [OUT_WEBSOCKET]
no -> [OUT_GRPC]
Q5: Is strict contract-first API design a priority?
yes -> [OUT_REST_OPENAPI]
no -> [OUT_REST]
[OUT_REST]: REST (pragmatic)
description: Standard HTTP verbs and JSON. Best default for CRUD-style integrations.
code: API_REST
[OUT_REST_OPENAPI]: REST + OpenAPI
description: REST with an OpenAPI 3.x spec-first workflow and auto-generated SDKs.
code: API_REST_OPENAPI
[OUT_GRAPHQL]: GraphQL
description: Flexible query language for complex frontend data requirements.
code: API_GRAPHQL
[OUT_GRPC]: gRPC / Protocol Buffers
description: High-performance binary RPC for internal microservice communication.
code: API_GRPC
[OUT_WEBSOCKET]: WebSocket / Server-Sent Events
description: Full-duplex protocol for real-time bidirectional messaging.
code: API_WEBSOCKET
Elimination Mode
In elimination mode (mode: elimination), answers narrow down a set of possible outcomes. Every answer specifies which outcomes remain after it is selected.
Set mode: elimination in the metadata, then list the surviving outcome IDs in brackets after each answer label.
dag: Database Engine Selector
version: 1.0.0
entry: Q1
mode: elimination
Q1: What best describes your primary data access pattern?
A: Key-value look-ups [REDIS, DYNAMO, MONGO]
B: Relational rows with JOIN [POSTGRES, MYSQL, AURORA]
C: Flexible-schema document store [MONGO, COSMOS, FIRESTORE]
D: Time-series measurements [INFLUX, TIMESCALE]
Q2: What is your consistency requirement?
when: Q1=B
A: Strong — all reads reflect the latest write [POSTGRES, AURORA, MYSQL]
B: Eventual — slight lag is acceptable [AURORA, COSMOS]
[POSTGRES]: PostgreSQL
color: #336791
description: Advanced open-source relational database with full ACID guarantees.
code: DB_POSTGRES
[MYSQL]: MySQL
color: #4479A1
description: Widely adopted open-source RDBMS with large ecosystem support.
code: DB_MYSQL
Elimination Answer Syntax
There are two equivalent ways to express elimination answers:
Inline — outcome list on the same line as the answer label:
A: Answer text [OUT_A, OUT_B, OUT_C]
routes: block — list the survivors in a sub-block under the question (useful when the outcome lists are long):
Q2: What is your consistency requirement?
routes:
A: Strong — all reads reflect the latest write -> [POSTGRES, AURORA, MYSQL]
B: Eventual — slight lag is acceptable -> [AURORA, COSMOS]
Both forms produce identical behaviour. Inline is more compact; routes: blocks are easier to read when each answer maps to many outcomes.
Conditional Questions (when:)
A question with a when: clause is only shown to the user when the referenced question has the specified answer. This lets you create branching sub-flows within an elimination tree.
Q3: Do you need horizontal write-scaling?
when: Q2=A
A: Yes — multi-region active-active [AURORA, COSMOS]
B: No — single primary writer [POSTGRES, MYSQL, AURORA]
Routes Section
An optional top-level routes: block at the end of the file documents every explicit end-to-end path through the tree. Each line maps a combination of question/answer pairs to a final outcome.
Note: this is a top-level documentation block, distinct from the per-question
routes:sub-block used in elimination mode (described above).
routes:
Q1=A, Q2=no, Q5=no -> [OUT_REST]
Q1=A, Q2=no, Q5=yes -> [OUT_REST_OPENAPI]
Q1=A, Q2=yes, Q4=no -> [OUT_GRPC]
Q1=A, Q2=yes, Q4=yes -> [OUT_WEBSOCKET]
Q1=C, Q3=yes -> [OUT_GRAPHQL]
- Conditions are separated by commas:
Q1=A, Q2=no - Use
|to allow multiple answer values for one question:Q1=A|B - The path ends with
-> [OUTCOME_ID]
Comments
Lines (or portions of lines) beginning with # are treated as comments and ignored by the parser. Hex color values like #FF9900 are not treated as comments.
# This is a full-line comment
dag: My Tree # inline comment on a metadata field
hint: see docs # inline comment on a question sub-field
color: #FF9900 # hex colour — NOT stripped as a comment
Complete File Reference
# ── Metadata ────────────────────────────────────────────────
dag: <Display Name> # required
version: 1.0.0 # required (semver recommended)
entry: Q1 # required — ID of the first question
mode: decision | elimination # optional, default: decision
description: <short description> # optional
image: https://<url> # optional — https:// hero image URL only
tags: <tag1>, <tag2> # optional — comma-separated topic tags
# ── Questions ────────────────────────────────────────────────
# Question IDs: A-Za-z0-9_ (mixed case, underscores allowed)
Q1: <Question text>
hint: <optional context hint>
when: <QID>=<answer> # optional — elimination mode only, one per question
# Binary answers:
yes -> Q2
no -> [OUT_ID]
# Choice answers (A–F):
A: <Label> -> Q2
B: <Label> -> [OUT_ID]
# Elimination inline routes:
A: <Label> [OUT_A, OUT_B]
# Elimination routes block (alternative to inline):
routes:
A: <Label> -> [OUT_A, OUT_B]
B: <Label> -> [OUT_C]
# ── Outcomes ─────────────────────────────────────────────────
# Outcome IDs: A-Z0-9_- (uppercase only, hyphens allowed)
[OUT_ID]: <Display Label>
description: <explanation text>
code: MACHINE_CODE
color: #RRGGBB
# ── Routes (optional top-level path documentation) ───────────
routes:
Q1=A, Q2=yes -> [OUT_ID]
Q1=B|C, Q2=no -> [OUT_OTHER]
Tips
- Keep question text short and scannable
- Use
UPPER_SNAKE_CASEfor outcome IDs (e.g.[OUT_ENTERPRISE]) and prefix-style IDs for questions (e.g.Q1,CHECK_DB) - Use
hint:to explain jargon or give users the context they need to answer - Use
code:on outcomes to make results machine-readable via the embed widget and the API - Test every path using the Path View
- Start with decision mode; switch to elimination when multiple criteria independently filter a fixed set of options
Decision Tree Generator vs Lucidchart: Why Choose DSL?
For technical teams evaluating a decision tree generator vs Lucidchart, the DSL approach offers concrete advantages that go beyond feature lists.
Version Control and Change Tracking
A Lucidchart decision tree is a proprietary visual file. Tracking changes over time means comparing two screenshots or relying on Lucidchart's own version history. A DrawDecisionTree DSL file is plain text — commit it to Git and get a full, readable diff of every change, when it was made, and by whom.
-Q2: What's your team size?
- A: 1-10 people [STARTER]
- B: 10+ people [ENTERPRISE]
+Q2: What's your team size?
+ A: 1-10 people [STARTER]
+ B: 11-50 people [PROFESSIONAL]
+ C: 50+ people [ENTERPRISE]
This kind of human-readable change history is impossible with Lucidchart's visual format.
Programmatic Generation
Because the DSL is plain text with a consistent structure, decision trees can be generated programmatically. A script can read from a database of rules and output a valid .dag file. This is useful for organisations with many similar decision trees (different product lines, different regions) that share a common structure.
With Lucidchart, programmatic generation would require using their API to create diagram elements — far more complex than writing structured text.
Collaboration Across Technical and Non-Technical Teams
The DSL syntax is designed to be readable by non-programmers. A subject matter expert who has never written code can review a .dag file and verify that the decision logic is correct. They can suggest changes in a PR comment or edit the text directly.
Lucidchart collaboration requires everyone to have a Lucidchart account and understand the visual editor. DSL collaboration works with any text editor, any code review system, and any team member who can read plain English.
Speed of Authoring and Iteration
Building a 10-question decision tree in Lucidchart's visual editor takes 30–60 minutes of placing shapes and drawing connectors. Writing the same tree in DSL takes 10–15 minutes. Each subsequent change is proportionally faster — editing a text file versus repositioning diagram elements.
For teams that iterate frequently on decision logic (e.g., a support team that updates troubleshooting guides monthly as products change), the DSL approach is significantly more maintainable.
No Account Required
Building with DrawDecisionTree requires no account. Lucidchart requires account creation even for basic use. For teams that want to prototype quickly or share decision trees with external stakeholders who don't have accounts in a shared tool, DrawDecisionTree's no-login approach removes friction.
Decision Tree Maker vs Draw.io: Technical Precision Benefits
When comparing a decision tree maker vs Draw.io for technical use cases, the DSL format offers advantages that Draw.io's XML-based approach cannot match.
Human-Readable Source Format
Draw.io stores diagrams as XML — technically plain text, but not human-readable in any meaningful way. A Draw.io decision tree XML file looks like this:
<mxCell id="2" value="Is the device powered on?" style="rounded=1;..." vertex="1">
<mxGeometry x="160" y="80" width="200" height="60" as="geometry" />
</mxCell>
The DrawDecisionTree DSL for the same question looks like this:
Q1: Is the device powered on?
yes -> Q2
no -> [OUT_POWER]
The DSL is the specification. The XML is an implementation detail. For any team that needs to review, audit, or maintain decision logic, readable text is unambiguously better.
Linting and Validation
The DrawDecisionTree editor validates DSL in real time — highlighting errors as you type, before you've finished the file. Common mistakes (referencing a question that doesn't exist, using an undefined outcome ID, invalid syntax) are caught immediately.
Draw.io does not validate decision tree logic — it only ensures that the diagram is well-formed XML. A Draw.io decision tree with a missing outcome is visually identical to one that is correct.
Separation of Logic from Presentation
In Draw.io, the decision logic and the visual presentation (positions, styles, colors) are stored together. Changing the layout of a decision tree changes the file, even if the logic is identical.
In DrawDecisionTree DSL, logic and presentation are separated. The DSL defines the logic; the tool handles the layout automatically. This means the file only changes when the logic changes — making version diffs meaningful.
Automation and Templating
Organisations with many similar decision trees (different products, different regions, different languages) can automate DSL generation using templates and scripts. A Python or TypeScript script can produce valid DrawDecisionTree DSL files for hundreds of decision trees from a structured data source.
This level of automation is not practical with Draw.io's visual workflow.
Integration with Documentation Workflows
Because the DSL is plain text, it integrates naturally with documentation-as-code workflows. Decision trees can be stored alongside the code they document, reviewed in the same pull request system, and deployed as part of the same CI/CD pipeline.
Teams using Docs-as-Code (Markdown + Git + CI) find that DrawDecisionTree's plain text format fits naturally into their existing workflow. Draw.io's binary XML does not.
→ Start building in the editor
→ Browse the public decision tree directory
→ Publish your tree to make it discoverable and API-accessible