The browser is optional. With the LixBlogs CLI, a Markdown file can move from an idea to a reviewed, illustrated, published story without exposing a password or API key.
This guide walks through the complete workflow: installation, device login, drafting, Markdown formatting, metadata, images, collaboration, revision safety, publishing, and recovery. The commands work well for people at a terminal and for agents using structured output.
What the CLI controls
The CLI can manage the parts of a story that normally live across several screens:
- create and inspect drafts;
- write or replace Markdown content;
- set the title, punchline, slug, emoji, and topics;
- choose a personal or organization publication target;
- upload inline images and covers;
- generate images through a connected Pollinations account;
- invite editors and reviewers;
- inspect version history;
- publish, unpublish, trash, and restore stories;
- read creator analytics after publication.
It talks to the public LixBlogs API. It does not connect to the database, store a client secret, or ask you to paste a bearer token into the terminal.
flowchart LR
A[Markdown file] --> B[Validate locally]
B --> C[Create draft]
C --> D[Add metadata and media]
D --> E[Review latest revision]
E --> F[Publish]
F --> G[Analytics and revisions]1. Install the CLI
Install the published package globally so the lixblogs command is available from any directory:
npm install --global @elixpo/lixblogs-cli
lixblogs --helpThe CLI requires Node.js 18 or newer.
2. Sign in with device authorization
Start a login:
lixblogs loginThe terminal displays a verification URL and a short code. Press Enter to open the page locally, or copy the URL to another device. This also works on a VPS because no localhost callback or exposed port is required.
After approval, confirm the active identity:
lixblogs whoamiCredentials are stored in a local profile backed by the operating system credential store. By default, your Accounts username becomes the profile name.
To use more than one account:
lixblogs login
lixblogs profiles
lixblogs use another-username
lixblogs whoamiScope rule: request only the permissions needed by the workflow. Writing needslixblogs:blog:readandlixblogs:blog:write; publishing additionally needslixblogs:blog:publish.
If the current login lacks publishing permission, approve it again:
lixblogs login \
--scope openid \
--scope profile \
--scope lixblogs:profile:read \
--scope lixblogs:blog:read \
--scope lixblogs:blog:write \
--scope lixblogs:blog:publish3. Write the story in Markdown
Create a file such as my-story.md. This section demonstrates the Markdown structures writers use most often.
Start with a clear opening paragraph. Use bold text for emphasis, italics for a softer aside, and inline code for commands or identifiers.
A useful subsection
Use an unordered list when order does not matter:
- one focused point;
- another focused point;
- a final takeaway.
Use an ordered list for a sequence:
- inspect the current state;
- make one intentional change;
- verify the saved result.
A blockquote is useful for a warning, principle, or memorable statement.
Add a descriptive link to the LixBlogs documentation.
const message = "Fenced code blocks retain their language";
console.log(message);sequenceDiagram
participant Writer
participant CLI
participant LixBlogs
Writer->>CLI: create or edit Markdown
CLI->>LixBlogs: authenticated API request
LixBlogs-->>CLI: draft and strong revisionKeep the story body in the file and the publishing metadata in CLI flags. That separation makes the prose easy to review in Git while keeping the command explicit.
4. Validate before creating anything
Use a dry run first:
lixblogs blog create \
--file my-story.md \
--title "Writing and Publishing with the LixBlogs CLI" \
--subtitle "A complete terminal workflow from Markdown draft to published story." \
--slug writing-with-the-lixblogs-cli \
--emoji "⌨️" \
--tag lixblogs \
--tag cli \
--tag markdown \
--tag publishing \
--dry-runA dry run performs local validation but does not create or update a story. The yellow status line is a declaration that no write happened.
5. Create the draft and capture its ID
For an interactive terminal, remove --dry-run:
lixblogs blog create \
--file my-story.md \
--title "Writing and Publishing with the LixBlogs CLI" \
--subtitle "A complete terminal workflow from Markdown draft to published story." \
--slug writing-with-the-lixblogs-cli \
--emoji "⌨️" \
--tag lixblogs \
--tag cli \
--tag markdown \
--tag publishingFor a script or agent, use stable JSON and capture the canonical UUID:
CREATE_RESULT="$(lixblogs blog create \
--file my-story.md \
--title "Writing and Publishing with the LixBlogs CLI" \
--subtitle "A complete terminal workflow from Markdown draft to published story." \
--slug writing-with-the-lixblogs-cli \
--emoji "⌨️" \
--tag lixblogs \
--tag cli \
--tag markdown \
--tag publishing \
--json \
--no-input)"
BLOG_ID="$(printf '%s' "$CREATE_RESULT" | jq -r '.id')"
printf 'Draft: %s\n' "$BLOG_ID"--json --no-input is machine mode. It never prompts, never emits animation into standard output, and returns a predictable JSON envelope.
6. Inspect and edit the current draft
List drafts:
lixblogs blog list --status draft --limit 20Fetch one story, including its Markdown and revision:
lixblogs blog get "$BLOG_ID" --json --no-inputAfter changing my-story.md, validate and apply the edit:
lixblogs blog edit "$BLOG_ID" --file my-story.md --dry-run
lixblogs blog edit "$BLOG_ID" --file my-story.mdTo update metadata without replacing the body:
lixblogs blog edit "$BLOG_ID" \
--title "The Complete LixBlogs CLI Writing Guide" \
--subtitle "Draft, illustrate, review, and publish without leaving the terminal." \
--tag lixblogs \
--tag cli \
--tag markdown \
--tag creator-tools \
--allow-commentsRepeat --tag for every topic you want to retain: a metadata edit replaces the topic set. LixBlogs accepts up to five topics.
If you prefer your configured terminal editor:
EDITOR=nvim lixblogs blog edit "$BLOG_ID" --editor7. Add a cover or inline image
Upload an existing cover through the managed media pipeline:
lixblogs media upload \
--file cover.webp \
--blog "$BLOG_ID" \
--type cover \
--attach \
--upload-id cli-guide-cover-v1For an inline image, include a useful caption:
lixblogs media upload \
--file terminal-workflow.webp \
--blog "$BLOG_ID" \
--type inline \
--caption "The draft, review, and publishing workflow" \
--attachUploads are authenticated, compressed, metadata-stripped, quota-checked, and stored in the creator's selected LixBlogs or personal Cloudinary space.
Optional: generate an image with Pollinations
First connect Pollinations from Settings → Integrations, then inspect the connection:
lixblogs integrations pollinations-statusGenerate and attach one cover:
lixblogs media generate \
--prompt "Minimal editorial illustration of a terminal turning Markdown into a published story, violet and green accents, wide composition, no text, no logos" \
--model flux \
--width 1600 \
--height 500 \
--blog "$BLOG_ID" \
--type cover \
--attach \
--output cli-guide-cover.jpgImage generation spends Pollen from the connected Pollinations account. Keep --output: if attachment fails, upload the local file instead of generating and paying again.8. Publish personally or through an organization
Personal publishing is the default. To inspect organization and collection targets:
lixblogs org targets
lixblogs org collections ORG_IDAssign the draft to an organization before publishing:
lixblogs blog edit "$BLOG_ID" \
--publication "org:ORG_ID" \
--collection COLLECTION_IDOnly the owner can change the slug or publication target. Organization access is checked by the API; knowing an ID does not grant membership.
9. Invite a reviewer without publishing
Editorial access is separate from public visibility:
lixblogs collab invite "$BLOG_ID" \
--user reviewer-username \
--role viewer \
--yes
lixblogs collab list "$BLOG_ID"Use viewer for review, editor for content changes, and admin only when the collaborator must manage the editorial team.
10. Publish with an explicit preflight
Preview the latest state and capture its strong revision:
PREVIEW="$(lixblogs blog preview "$BLOG_ID" --json --no-input)"
ETAG="$(printf '%s' "$PREVIEW" | jq -r '.etag')"Validate the exact transition:
lixblogs blog publish "$BLOG_ID" \
--etag "$ETAG" \
--idempotency-key publish-cli-guide-v1 \
--dry-runWhen the title, content, cover, topics, target, and comments policy are correct, publish:
lixblogs blog publish "$BLOG_ID" \
--etag "$ETAG" \
--idempotency-key publish-cli-guide-v1 \
--yesPublishing is an explicit state change, so --yes is required. The green completion line confirms that the server accepted the transition.
If someone edits the story after the preview, publishing stops with revision_conflict instead of overwriting their work. Fetch the newest revision, review it, and make a new decision.
11. Revisions and recovery
Inspect retained snapshots:
lixblogs blog history "$BLOG_ID"Restore a specific version only after reviewing it:
lixblogs blog restore-version "$BLOG_ID" \
--version VERSION_ID \
--yesIf a normal edit conflicts, the CLI preserves both sides under .lixblogs-conflicts/:
- a local JSON file containing the intended update;
- a server Markdown file containing the newest remote content;
- the server ETag needed after reconciliation.
Nothing is force-overwritten automatically.
To take a published story back to draft:
lixblogs blog unpublish "$BLOG_ID" --yesTrash is recoverable:
lixblogs blog trash "$BLOG_ID" --yes
lixblogs blog list --status trashed
lixblogs blog restore "$BLOG_ID" --yesPermanent deletion is intentionally separate and requires an additional scope. Do not use it as routine cleanup.
12. After publishing
Read comments and reply:
lixblogs comment list "$BLOG_ID"
lixblogs comment reply "$BLOG_ID" \
--parent COMMENT_ID \
--content "Thanks—this is a useful clarification."Inspect the first 30 days of creator analytics:
lixblogs analytics query \
--range 30d \
--dimension overview
lixblogs analytics export \
--range 30d \
--dimension timeline \
--format csv \
--output cli-guide-analytics.csvAnalytics is aggregate-only and does not expose visitor identities.
A compact daily workflow
Once authentication is configured, the core loop is deliberately small:
BLOG_ID="YOUR_BLOG_ID"
lixblogs blog edit "$BLOG_ID" --file my-story.md --dry-run && \
lixblogs blog edit "$BLOG_ID" --file my-story.md && \
lixblogs blog preview "$BLOG_ID" && \
lixblogs blog publish "$BLOG_ID" --dry-run && \
lixblogs blog publish "$BLOG_ID" --yesUse interactive output when a person is watching. Use --json --no-input when an agent or shell script needs a stable contract.
Final checklist
- [x]
lixblogs whoamishows the intended account.
- [x] The draft has the right title, punchline, slug, emoji, and topics.
- [x] Links and code examples were checked.
- [x] Images have useful context and the cover is attached.
- [x] The publication and collection target are correct.
- [x] The latest revision was previewed.
- [x] The publish dry run succeeded.
- [x]
--yeswas used only after the final review.
The CLI is not meant to turn publishing into one opaque command. Its job is to make every important step scriptable, inspectable, and safe—while keeping Markdown at the center of the writing process.

