Finally, years after topic was opened, there is a proper OpenAPI 3.0 schema for REST API using our friend /console/inspect.
https://tikoci.github.io/restraml/7.22.1/openapi.json (routeros.npk only)
https://tikoci.github.io/restraml/7.22.1/extra/openapi.json (with all extra-packages for CHR, so most of them)
Only versions newer than 7.22.1 will have OpenAPI 3 schemes, RAML remains for older versions, and still be built. All new beta/rc and releases will include OpenAPI, so you can change the URL above to match versions (within ~24 hours of a new public RouterOS build).
RAML really only works in Postman, and slowly. Modern tools want OpenAPI format. Now OpenAPI 3 schema is still big (11MB, 5000+ methods)... so it takes most visual tools a while to render still. CLI / CI validation using the OAS3 schema works better, since it not 5000 x x UI controls to track/draw.
NEW REST API Explorer Webpage
There is also new Scalar-based web viewer of the schema.


"Test Request" requires CORS Proxy
You cannot "run" anything since RouterOS does not support "CORS" in REST API. You can setup a "CORS Proxy" to allow using the "Test Request" control. Search forum, you can use ngnix, Traefik, or Caddy server - all should work with the new "API Explorer". I tested Traefik as CORS Proxy, and webpage works with the requests.
Doc Links Included
The OpenAPI 3 version adds doc links to help.mikrotik.com in selected spots. It just contains links and it pretty conservative in matching them, so should generally be right... but many elements do have some doc page that could be links. This is a work in progress.
The doc links here from a SQLite database after converting the help.mikrotik.com's "monthly" PDF/HTML offline to FTS5-enabled SQLite tables, breaking out fragments, "callout", and attribute tables.
The database used is actual built in tikoci/rosetta, which is also an MCP Server that allows LLM agents can more easily consume MikrotTik's help.mikrotik.com docs. See GitHub - tikoci/rosetta: MCP Server with RouterOS docs + commands + products + changelogs, using SQLite-as-RAG, sourced from MikroTik · GitHub for details.
NOTE OpenAPI schema generation does not use an LLM for the docs. Rather it shares the same database as the MCP Server. The doc links come the table generated by Python scripts that parse Confluence HTML into SQLite.
How REST schemas are made?
For every RouterOS build, the following process essentially calls /console/inspect with ~50K request. The exact process was captured by CoPilot in GraphViz (which forum supports via [graphviz] BBtags).
digraph "How a RouterOS OpenAPI Schema Is Made" {
rankdir=TB;
fontname="Helvetica";
fontsize=13;
compound=true;
newrank=true;
nodesep=0.4;
ranksep=0.6;
bgcolor="white";
label=<<B><FONT POINT-SIZE="18">How a RouterOS OpenAPI Schema Is Made</FONT></B>>;
labelloc=t;
labeljust=c;
node [fontname="Helvetica", fontsize=11, style="filled,rounded", shape=box, margin="0.15,0.1"];
edge [fontname="Helvetica", fontsize=9, color="#546e7a", arrowsize=0.8];
subgraph cluster_trigger {
label=<<B>⏰ 1 · VERSION DETECTION</B><BR/><FONT POINT-SIZE="9">Daily at 4 AM UTC</FONT>>;
style="rounded,filled"; fillcolor="#fff3e0"; color="#e65100"; penwidth=2;
fontname="Helvetica"; fontsize=12;
cron [label=<<B>🕐 auto.yaml</B><BR/>Cron Trigger>, fillcolor="#ff6d00", fontcolor=white, color="#e65100"];
channels [label=<<B>Query 4 MikroTik Channels</B><BR/><I>stable · testing · development · long-term</I><BR/><FONT POINT-SIZE="9">upgrade.mikrotik.com/routeros/NEWESTa7.*</FONT>>, fillcolor="#ff6d00", fontcolor=white, color="#e65100"];
check [label=<<B>Version<BR/>already built?</B>>, shape=diamond, fillcolor="#ff6d00", fontcolor=white, color="#e65100", width=1.5];
skip [label="✓ Skip", fillcolor="#fff9c4", fontcolor="#333", color="#f9a825", style="filled,rounded,dashed"];
dispatch [label=<<B>Dispatch<BR/>Build Workflow</B>>, fillcolor="#ff6d00", fontcolor=white, color="#e65100"];
cron -> channels -> check;
check -> skip [label="openapi.json\nexists", fontsize=8];
check -> dispatch [label="Missing", fontsize=8, penwidth=2];
}
subgraph cluster_infra {
label=<<B>🖥️ 2 · SPIN UP VIRTUAL ROUTER</B><BR/><FONT POINT-SIZE="9">GitHub Actions Runner + QEMU/KVM</FONT>>;
style="rounded,filled"; fillcolor="#e3f2fd"; color="#0d47a1"; penwidth=2;
fontname="Helvetica"; fontsize=12;
qemu_install [label=<<B>Install QEMU</B><BR/><FONT FACE="Courier" POINT-SIZE="9">apt install qemu-system-x86 qemu-utils</FONT>>, fillcolor="#1565c0", fontcolor=white, color="#0d47a1"];
kvm [label=<<B>Enable KVM</B><BR/><I>Hardware acceleration</I><BR/><FONT POINT-SIZE="9">GitHub runners have /dev/kvm</FONT>>, fillcolor="#1565c0", fontcolor=white, color="#0d47a1"];
dl_chr [label=<<B>Download CHR Disk Image</B><BR/><FONT FACE="Courier" POINT-SIZE="9">chr-{ver}.vdi.zip</FONT><BR/><FONT POINT-SIZE="9">download.mikrotik.com → cdn.mikrotik.com</FONT>>, fillcolor="#1565c0", fontcolor=white, color="#0d47a1"];
convert [label=<<B>Convert VDI → QCOW2</B><BR/><FONT FACE="Courier" POINT-SIZE="9">qemu-img convert -f vdi -O qcow2</FONT>>, fillcolor="#1565c0", fontcolor=white, color="#0d47a1"];
launch [label=<<B>Launch RouterOS in QEMU</B><BR/><I>256 MB RAM · virtio disk · user-mode net</I><BR/><FONT POINT-SIZE="9">Port 9180→80 (REST) · Port 9122→22 (SSH)</FONT>>, fillcolor="#1565c0", fontcolor=white, color="#0d47a1"];
wait [label=<<B>⏳ Wait for REST API</B><BR/><I>up to 5 min</I>>, shape=diamond, fillcolor="#1565c0", fontcolor=white, color="#0d47a1", width=1.8];
ready [label=<<B>✅ RouterOS REST API Ready</B><BR/><FONT FACE="Courier" POINT-SIZE="9">http://localhost:9180/rest</FONT>>, fillcolor="#d32f2f", fontcolor=white, color="#b71c1c"];
qemu_install -> kvm -> dl_chr -> convert -> launch -> wait;
wait -> ready [label="curl succeeds", fontsize=8];
}
subgraph cluster_crawl {
label=<<B>🔍 3 · CRAWL THE ENTIRE API TREE</B><BR/><FONT POINT-SIZE="9">rest2raml.js (Bun runtime)</FONT>>;
style="rounded,filled"; fillcolor="#e8f5e9"; color="#1b5e20"; penwidth=2;
fontname="Helvetica"; fontsize=12;
start_crawl [label=<<B>Start at Root Path</B><BR/><FONT FACE="Courier" POINT-SIZE="9">POST /rest/console/inspect</FONT><BR/><FONT FACE="Courier" POINT-SIZE="9">{request: "child", path: ""}</FONT>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
subgraph cluster_loop {
label=<<B>🔄 Recursive Tree Walk</B><BR/><FONT POINT-SIZE="9">Every command, every argument</FONT>>;
style="rounded,dashed"; fillcolor="#c8e6c9"; color="#1565c0"; penwidth=2;
fontname="Helvetica"; fontsize=10;
fetch [label=<<B>Fetch Children</B><BR/><FONT FACE="Courier" POINT-SIZE="9">{request: "child", path: "ip,address,..."}</FONT><BR/><I>Returns: name + node-type (dir│cmd│arg)</I>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
checktype [label=<<B>Node Type?</B>>, shape=diamond, fillcolor=white, fontcolor="#1565c0", color="#1565c0", style="filled,dashed", width=1.2];
recurse [label=<<B>📁 Directory</B><BR/>Recurse deeper>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
syntax [label=<<B>⚡ Command / Argument</B><BR/><FONT FACE="Courier" POINT-SIZE="9">{request: "syntax"}</FONT><BR/><I>Returns type info:</I><BR/><FONT POINT-SIZE="9">"0..65535" · "IP address"<BR/>"string, max length 45"</FONT>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
crashwarn [label=<<B>⚠️ Skip Crash Paths</B><BR/><I>where · do · else · rule</I><BR/><I>command · on-error</I>>, fillcolor="#fff9c4", fontcolor="#333", color="#f9a825", style="filled,rounded,dashed"];
fetch -> checktype;
checktype -> recurse [label="dir", fontsize=8];
checktype -> syntax [label="cmd / arg", fontsize=8];
recurse -> fetch [label="~2000+ paths", fontsize=8, style=dashed, constraint=false];
syntax -> fetch [style=dashed, constraint=false];
crashwarn -> recurse [style=dotted, arrowhead=none, color="#f9a825"];
}
inspect_json [label=<<B>💾 inspect.json</B><BR/><I>Complete API tree</I><BR/><FONT POINT-SIZE="9">~5000 nodes</FONT>>, shape=cylinder, fillcolor="#37474f", fontcolor=white, color="#263238"];
raml_out [label=<<B>📄 schema.raml</B><BR/><I>RAML 1.0 schema</I>>, shape=cylinder, fillcolor="#37474f", fontcolor=white, color="#263238"];
start_crawl -> fetch;
syntax -> inspect_json [style=bold];
syntax -> raml_out [style=bold];
}
raml_validate [label=<<B>✔️ Validate RAML 1.0</B><BR/><FONT FACE="Courier" POINT-SIZE="9">node validraml.cjs</FONT><BR/><I>webapi-parser</I>>, fillcolor="#6a1b9a", fontcolor=white, color="#4a148c"];
subgraph cluster_openapi {
label=<<B>⚙️ 4 · GENERATE OpenAPI 3.0</B><BR/><FONT POINT-SIZE="9">deep-inspect.ts (Bun runtime)</FONT>>;
style="rounded,filled"; fillcolor="#e8f5e9"; color="#1b5e20"; penwidth=2;
fontname="Helvetica"; fontsize=12;
parse_tree [label=<<B>Parse Inspect Tree</B><BR/><I>Map RouterOS commands<BR/>→ REST operations</I>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
map_ops [label=<<B>Map Commands → HTTP Methods</B><BR/><FONT FACE="Courier" POINT-SIZE="10">get → GET · set → PATCH</FONT><BR/><FONT FACE="Courier" POINT-SIZE="10">add → PUT · remove → DELETE</FONT>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
parse_types [label=<<B>Parse Type Descriptions</B><BR/><FONT POINT-SIZE="9">"0..4294967295" → integer min/max</FONT><BR/><FONT POINT-SIZE="9">"IP address" → string format:ipv4</FONT><BR/><FONT POINT-SIZE="9">"time interval" → string (duration)</FONT>>, fillcolor="#2e7d32", fontcolor=white, color="#1b5e20"];
openapi_raw [label=<<B>📋 openapi.json</B><BR/><I>OpenAPI 3.0 schema</I><BR/><FONT POINT-SIZE="9">~1800 paths · ~3400 params</FONT>>, shape=cylinder, fillcolor="#37474f", fontcolor=white, color="#263238"];
parse_tree -> map_ops -> parse_types -> openapi_raw;
}
openapi_validate [label=<<B>✔️ Validate OpenAPI 3.0</B><BR/><FONT FACE="Courier" POINT-SIZE="9">bun validate-openapi.ts</FONT><BR/><I>swagger-parser</I>>, fillcolor="#6a1b9a", fontcolor=white, color="#4a148c"];
subgraph cluster_enrich {
label=<<B>📚 5 · ENRICH WITH DOCUMENTATION</B><BR/><FONT POINT-SIZE="9">enrich-openapi.ts + Rosetta SQLite DB</FONT>>;
style="rounded,filled"; fillcolor="#fbe9e7"; color="#bf360c"; penwidth=2;
fontname="Helvetica"; fontsize=12;
rosetta_dl [label=<<B>Download Rosetta SQLite DB</B><BR/><FONT FACE="Courier" POINT-SIZE="9">ros-help.db.gz</FONT><BR/><I>Parsed from help.mikrotik.com</I><BR/><FONT POINT-SIZE="9">Official RouterOS manual pages</FONT>>, fillcolor="#e65100", fontcolor=white, color="#bf360c"];
match_paths [label=<<B>Match API Paths → Manual Pages</B><BR/><FONT POINT-SIZE="9">/ip/address → "IP Address" page</FONT><BR/><FONT POINT-SIZE="9">/interface/bridge → "Bridge" page</FONT><BR/><I>Multi-strategy: path · title · abbreviation</I>>, fillcolor="#e65100", fontcolor=white, color="#bf360c"];
add_docs [label=<<B>Add externalDocs Links</B><BR/><I>Each operation gets a link to<BR/>help.mikrotik.com documentation</I>>, fillcolor="#e65100", fontcolor=white, color="#bf360c"];
add_descs [label=<<B>Enrich Property Descriptions</B><BR/><I>Merge manual descriptions with<BR/>RouterOS syntax type info</I>>, fillcolor="#e65100", fontcolor=white, color="#bf360c"];
enriched [label=<<B>📋 openapi.json ✨</B><BR/><I>Enriched with docs links</I><BR/><FONT POINT-SIZE="9">~40% operations linked<BR/>~36% args described</FONT>>, shape=cylinder, fillcolor="#37474f", fontcolor=white, color="#263238"];
rosetta_dl -> match_paths -> add_docs -> add_descs -> enriched;
}
subgraph cluster_publish {
label=<<B>🚀 6 · PUBLISH TO GITHUB PAGES</B>>;
style="rounded,filled"; fillcolor="#e0f2f1"; color="#004d40"; penwidth=2;
fontname="Helvetica"; fontsize=12;
commit [label=<<B>Git Commit to main</B><BR/><FONT FACE="Courier" POINT-SIZE="9">docs/{version}/</FONT><BR/><I>schema.raml · inspect.json · openapi.json</I>>, fillcolor="#00695c", fontcolor=white, color="#004d40"];
push [label=<<B>Push with Retry + Rebase</B><BR/><I>Handles concurrent builds</I><BR/><FONT POINT-SIZE="9">(base + extra packages in parallel)</FONT>>, fillcolor="#00695c", fontcolor=white, color="#004d40"];
pages [label=<<B>🌐 GitHub Pages</B><BR/><B>tikoci.github.io/restraml</B><BR/><I>Interactive schema explorer<BR/>Diff tool · Command lookup</I>>, fillcolor="#00695c", fontcolor=white, color="#004d40", penwidth=2];
commit -> push -> pages;
}
dispatch -> qemu_install [lhead=cluster_infra, penwidth=2, color="#546e7a"];
ready -> start_crawl [lhead=cluster_crawl, penwidth=2, color="#546e7a"];
raml_out -> raml_validate [penwidth=1.5];
inspect_json -> parse_tree [lhead=cluster_openapi, penwidth=2, color="#546e7a"];
raml_validate -> parse_tree [lhead=cluster_openapi, style=dashed, color="#6a1b9a"];
openapi_raw -> openapi_validate [penwidth=1.5];
openapi_validate -> rosetta_dl [lhead=cluster_enrich, penwidth=2, color="#546e7a"];
enriched -> commit [lhead=cluster_publish, penwidth=2, color="#546e7a"];
}
And that whole process essentially ends up back in GitHub here: https://github.com/tikoci/restraml/tree/main/docs which is "raw" downloads for all the schemas produced.
(GitHub Pages serves same directory as normal HTTPS that download link and API Explorer use)
OpenAPI 3 and other schemas are also downloadable from the main "Schema Download" page on my TIKOCI site: