Docs
How to ship markdown from your terminal
You already drafted the plan in your editor or with whatever assistant you use. Now get it on a share link before standup—no copy-paste into the browser required.
In the web editor or on a shared doc, open Export to download Markdown (.md), HTML, or plain text, print to PDF, or copy to the clipboard. Jump to the CLI guide or peek at MCP (coming soon).
CLI
Ship implementation plans and RFCs without leaving the terminal. One command, one share link—your team can read (and optionally edit or comment) in the browser.
Install
npm install -g @markdowny/cliRequires Node 18+. Verify with markdowny --version.
Login
Opens your browser (same sign-in as the web app). Your token is stored in ~/.config/markdowny/credentials.json.
markdowny loginTokens are managed in Settings—revoke anytime and sign in again.
Check or clear this machine. markdowny whoami prints the API URL and a masked token preview. markdowny logout removes ~/.config/markdowny/credentials.json—you’ll need to run login again (or set MARKDOWNY_TOKEN). Plain logout does not revoke the token on the server. Use markdowny logout --revoke to invalidate the current token on the server (cannot be undone) and clear the file, or revoke other tokens in Settings.
markdowny whoami
markdowny logout
markdowny logout --revokeCI & headless
Pipelines and servers don’t have a browser. Use an API token and environment variables instead of interactive login.
- Environment. Set
MARKDOWNY_TOKENto your token (when set, the CLI skips the on-disk credentials file). OptionalMARKDOWNY_API_URL— base URL, defaulthttps://www.markdowny.app(use www in production; apex redirects and drops Bearer tokens). OptionalMARKDOWNY_FOLDER_ID— default workspace folder for uploads when you don’t pass--folder-id(see Workspace folders). - Paste a token. On a machine where you can run the CLI once:
markdowny login --token mdy_…— same token format as in Settings. - No prompts. Use
--yeswithpublish/sync/watchso share mode and comments aren’t interactive. Combine with flags (e.g.--share-mode view --no-comments) for predictable output.
env:
MARKDOWNY_TOKEN: ${{ secrets.MARKDOWNY_TOKEN }}
# optional: MARKDOWNY_API_URL, MARKDOWNY_FOLDER_ID
run: npx markdowny publish ./CHANGELOG.md --yes --quiet --share-mode viewStore the token as a secret—never commit mdy_… to the repo.
Publish
Point at a file or pipe markdown on stdin. In a real terminal (not piped), we'll ask who can access the link (view only vs view and edit) and whether comments are allowed—same choices as the web Share dialog. Use --yes to skip those prompts for scripts and CI.
File paths must use a .md extension (case-insensitive) unless you pass --allow-any-extension. Piped stdin is not checked—only paths on disk.
markdowny publish ./plan.md
# After prompts, you'll see something like:
# Document uploaded to MarkDowny — "Your doc title"
# Open your doc: https://markdowny.app/d/abc123cat plan.md | markdowny publish --title "Sprint plan" --share-mode view --yes
markdowny publish ./draft.md --untitled --yes
markdowny publish ./spec.md --prompt-titleTitle & output
Title. By default the doc title is taken from the first markdown # heading. Use --title "Name" to set it explicitly, --untitled to force "Untitled" (ignore the heading), or --prompt-title to type the title in the terminal—the suggested default comes from the first heading; press Enter on an empty line for Untitled. Don't combine a non-empty --title with --untitled.
Output. By default you get a short success message plus the share URL. Use -q / --quiet to print only the URL (handy for shell scripts). Use --json for structured data, or --human for slightly friendlier copy.
markdowny publish ./notes.md --quiet --yes
# prints: https://markdowny.app/d/xyz
markdowny publish ./notes.md --json
# {"id":"...","url":"...","title":"..."}Workspace folders
Docs live in your MarkDowny workspace—optionally inside a folder (same folders you see in the sidebar). Create an empty folder on its own, list them, or attach uploads:
markdowny create-folder "Name"— create a folder at the workspace root with no doc upload. Use--parent <id>to nest under another folder (same as publishing with--parent-folder-id).--jsonor--quietfor scripts.--folder-id <id>— use an existing folder. Copy the folder id from the URL when you open it in the app (/workspace/<folderId>), or from the API.--create-folder "Name"— create a folder (at the workspace root) and put the doc(s) there.--parent-folder-id <id>— nest the new folder under this parent (only with--create-folder).MARKDOWNY_FOLDER_ID— machine-wide env default (overridden by CLI flags). Same priority as in CI & headless.defaultFolderIdin.markdowny/config.json— project default; see Project config.
Priority: explicit flags → MARKDOWNY_FOLDER_ID → project defaultFolderId → workspace root. Updating an existing doc with --id does not apply the project file default (only flags + env), so you don't move docs by accident.
markdowny create-folder "Q1 plans"
markdowny create-folder "Specs" --parent yourParentFolderId
markdowny foldersProject config (.markdowny/config.json)
File path: <root>/.markdowny/config.json, where <root> is the directory you pass to sync / watch. The CLI creates and updates this file when syncing—it maps each local markdown file to a remote doc id. publish only loads this file when resolving a project directory (walking up from the current directory) for defaultFolderId on new docs.
| Key | Values / type | What it does |
|---|---|---|
| defaultFolderId | string or null (optional) | MarkDowny workspace folder for new uploads from this project. Used by sync/watch and by publish for new docs only (not with --id updates). |
| defaultVisibility | public | private (default public) | New docs from sync / watch: whether the doc is public. Existing mappings keep updating the same remote doc. |
| defaultShareMode | view | edit (default view) | Default link permission for synced files unless overridden per file (below) or by interactive/flag defaults on first run. |
| defaultCommentsDisabled | boolean (default false) | Default for new synced docs: when true, comments are off. |
| files | object (required; may start empty {}) | Keys are paths to .md files relative to the sync root (forward slashes). Each value is { "id": "…" } plus optional shareMode and commentsDisabled for that file. Populated automatically after the first successful sync. |
{
"defaultFolderId": "abc12xyz90",
"defaultVisibility": "public",
"defaultShareMode": "view",
"defaultCommentsDisabled": false,
"files": {
"README.md": { "id": "docId1", "shareMode": "view", "commentsDisabled": false },
"plans/spec.md": { "id": "docId2" }
}
}List & read
Browse what you already have in your workspace—without opening the app. Use folders to pick a parent folder id, then filter docs with --folder-id.
markdowny docs— list document ids, titles, folder, and last updated. Add--jsonfor a shortcontentPreviewper row (not the full body).markdowny folders— folders directly under the workspace root by default;--parent <id>for children of another folder.markdowny show <id>(cat) — print the markdown body to stdout (pipe into a file or pager). Use--jsonfor the full document object includingcontent.markdowny comments <id>— print inline comment threads (selection quote + messages with author labels). Empty docs printno comments found.--jsonmatchesGET /api/documents/<id>/comments.
markdowny folders
markdowny docs --folder-id root -q "plan"
markdowny show xYz123AbC > ./backup.md
markdowny comments xYz123AbCPrivate docs only appear for your account; public docs owned by others are readable via show or comments if you have the id and the doc is public (or yours).
Sync & watch
Upload every .md file under a folder to your account—like running publish on each file. First run: one new doc per file. Later runs: update those same docs so you don’t create duplicates.
MarkDowny stores which local path maps to which remote doc in .markdowny/config.json inside that folder (full reference).
sync— upload once and exitwatch— keep running and re-upload when you save files
markdowny sync ./docs
markdowny watch ./docsHandy for a docs/ or plans/ tree in a repo. This flow is upload only—edits you make in the browser don't write back to your .md files.
Flags
| Flag | Description |
|---|---|
| --title <string> | Set doc title (omit to use first # heading) |
| --untitled | Save as "Untitled" (skip first heading) |
| --prompt-title | Prompt for title (empty = Untitled; skipped with --yes) |
| --share-mode view|edit | Who the link allows: view or edit |
| --no-comments | Disable comments on the doc |
| --private | Save without making public |
| --allow-any-extension | Allow a non-.md file path (default: only .md; stdin unchanged) |
| --id <id> | Update an existing doc |
| --folder-id <id> | Put doc(s) in this workspace folder |
| --create-folder <name> | Create folder, then assign doc(s) |
| --parent-folder-id <id> | With --create-folder, nest under this parent |
| --yes | Skip share/comments prompts (and --prompt-title) |
| -q, --quiet | Print only the share URL |
| --json | JSON: id, url, title, shareMode, … |
| --human | Alternate friendly success message |
Troubleshooting
- Free plan limit — you'll see a 403 with an upgrade hint; fewer docs or go Pro.
- Unauthorized after login (production) — if credentials use
https://markdowny.app(no www), API calls redirect to www and drop the Bearer token. Usehttps://www.markdowny.appin~/.config/markdowny/credentials.jsonor runmarkdowny login --api-url https://www.markdowny.app. - Browser didn't return to terminal — allow localhost callback or use
markdowny login --token. - Leaked token — revoke in Settings or run
markdowny logout --revokeon machines that have that credential, thenmarkdowny loginagain with a new token.
MCP
Coming soonModel Context Protocol integration is on the roadmap—so tools like Cursor and Claude Desktop can create and update MarkDowny docs without you copy-pasting markdown by hand.
Until then, the CLI is your best friend for terminal-native workflows.