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
npm install -g @markdowny/cli

Requires 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 login

Tokens 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 --revoke

CI & headless

Pipelines and servers don’t have a browser. Use an API token and environment variables instead of interactive login.

  • Environment. Set MARKDOWNY_TOKEN to your token (when set, the CLI skips the on-disk credentials file). Optional MARKDOWNY_API_URL — base URL, default https://www.markdowny.app (use www in production; apex redirects and drops Bearer tokens). Optional MARKDOWNY_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 --yes with publish / sync / watch so share mode and comments aren’t interactive. Combine with flags (e.g. --share-mode view --no-comments) for predictable output.
GitHub Actions (example)
env:
  MARKDOWNY_TOKEN: ${{ secrets.MARKDOWNY_TOKEN }}
  # optional: MARKDOWNY_API_URL, MARKDOWNY_FOLDER_ID
run: npx markdowny publish ./CHANGELOG.md --yes --quiet --share-mode view

Store 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.

Interactive (TTY)
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/abc123
Non-interactive / piping
cat plan.md | markdowny publish --title "Sprint plan" --share-mode view --yes

markdowny publish ./draft.md --untitled --yes
markdowny publish ./spec.md --prompt-title

Title & 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.

Examples
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).--json or --quiet for 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.
  • defaultFolderId in .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.

Folders only
markdowny create-folder "Q1 plans"
markdowny create-folder "Specs" --parent yourParentFolderId
markdowny folders

Project 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.

KeyValues / typeWhat it does
defaultFolderIdstring 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).
defaultVisibilitypublic | private (default public)New docs from sync / watch: whether the doc is public. Existing mappings keep updating the same remote doc.
defaultShareModeview | edit (default view)Default link permission for synced files unless overridden per file (below) or by interactive/flag defaults on first run.
defaultCommentsDisabledboolean (default false)Default for new synced docs: when true, comments are off.
filesobject (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.
Example
{
  "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 --json for a short contentPreview per 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 --json for the full document object including content.
  • markdowny comments <id> — print inline comment threads (selection quote + messages with author labels). Empty docs print no comments found. --json matches GET /api/documents/<id>/comments.
Examples
markdowny folders
markdowny docs --folder-id root -q "plan"
markdowny show xYz123AbC > ./backup.md
markdowny comments xYz123AbC

Private 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 exit
  • watch — keep running and re-upload when you save files
markdowny sync ./docs
markdowny watch ./docs

Handy 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

FlagDescription
--title <string>Set doc title (omit to use first # heading)
--untitledSave as "Untitled" (skip first heading)
--prompt-titlePrompt for title (empty = Untitled; skipped with --yes)
--share-mode view|editWho the link allows: view or edit
--no-commentsDisable comments on the doc
--privateSave without making public
--allow-any-extensionAllow 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
--yesSkip share/comments prompts (and --prompt-title)
-q, --quietPrint only the share URL
--jsonJSON: id, url, title, shareMode, …
--humanAlternate 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. Use https://www.markdowny.app in ~/.config/markdowny/credentials.json or run markdowny 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 --revoke on machines that have that credential, then markdowny login again with a new token.

MCP

Coming soon

Model 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.