Quickstart
Create an API key, then publish with the CLI, API, Node SDK, or an agent.
Start in the dashboard, create keys, then publish one draft through the CLI, API, Node SDK, or agent skill.
Choose your quickstart
Start with the surface that matches the work you want to complete:
Publish from the CLI
Create supporting resources and a reviewed draft from a terminal.
Call the REST API
Create and deliver posts from any technology that sends HTTP requests.
Build with the Node SDK
Use typed resource methods from Node.js 20+ code.
Publish with an AI agent
Install the skill, inspect drafts, create content, and wait for approval.
Render a blog frontend
Read published content with a public key and render it in React.
Get an API key
Create a public key for delivery reads and a private key for trusted publishing.
- Open API keys in the dashboard.
- Create a public key with read access for your blog frontend.
- Create a private key with only the write permissions this quickstart needs.
- Save the private value immediately in your shell or secret manager; it is shown only when created.
See API keys before choosing permissions for production.
Use the CLI
Configure a trusted key.
cli-blog config set --api-key "$CLI_BLOG_API_KEY"Create a draft.
AUTHOR_ID=$(cli-blog authors create --public-name "Maya Chen" --slug maya-chen --json | jq -r '.id')
CATEGORY_ID=$(cli-blog categories create --name "Engineering" --slug engineering --locale en-US --json | jq -r '.id')
TAG_ID=$(cli-blog tags create --name "Launch Notes" --slug launch-notes --locale en-US --json | jq -r '.id')
POST_JSON=$(cli-blog posts create \
--title "Shipping an API-first blog" \
--body-markdown "## What shipped
We added a durable publishing workflow to our product." \
--author-ids "$AUTHOR_ID" \
--category-ids "$CATEGORY_ID" \
--tag-ids "$TAG_ID" \
--json)
POST_ID=$(printf '%s' "$POST_JSON" | jq -r '.id')
POST_VERSION=$(printf '%s' "$POST_JSON" | jq -r '.version')
printf 'Draft created: %s\n' "$POST_ID"Publish after review.
cli-blog posts publish "$POST_ID" --expected-version "$POST_VERSION" --jsonPublishing is a status change under the hood. The command above is a shortcut for:
cli-blog posts update "$POST_ID" --status published --expected-version "$POST_VERSION" --jsonYou can also publish immediately on create by passing --status published, though draft-first is safer for human or agent review.
Use public keys for published-content reads. Use private keys for create, update, publish, schedule, and delete commands. Read API keys for storage, permissions, and rotation guidance.
Call the API
Read published posts with a public key.
curl -sS "https://api.cli-blog.com/v1/posts?locale=en-US&fields=summary&include=authors,tags" \
-H "x-api-key: $CLI_BLOG_PUBLIC_KEY"Create a complete draft with a private key. This terminal example uses jq to pass each returned ID into the next request.
AUTHOR_ID=$(curl -sS "https://api.cli-blog.com/v1/authors" \
-H "content-type: application/json" \
-H "x-api-key: $CLI_BLOG_API_KEY" \
-d '{
"public_name": "Maya Chen",
"slug": "maya-chen"
}' | jq -r '.id')
CATEGORY_ID=$(curl -sS "https://api.cli-blog.com/v1/categories" \
-H "content-type: application/json" \
-H "x-api-key: $CLI_BLOG_API_KEY" \
-d '{ "name": "Engineering", "slug": "engineering", "locale": "en-US" }' | jq -r '.id')
TAG_ID=$(curl -sS "https://api.cli-blog.com/v1/tags" \
-H "content-type: application/json" \
-H "x-api-key: $CLI_BLOG_API_KEY" \
-d '{ "name": "Launch Notes", "slug": "launch-notes", "locale": "en-US" }' | jq -r '.id')
POST_JSON=$(curl -sS "https://api.cli-blog.com/v1/posts" \
-H "content-type: application/json" \
-H "x-api-key: $CLI_BLOG_API_KEY" \
-d "$(jq -n \
--arg author "$AUTHOR_ID" \
--arg category "$CATEGORY_ID" \
--arg tag "$TAG_ID" \
'{
title: "Shipping an API-first blog",
locale: "en-US",
body_markdown: "## What shipped\n\nWe added a durable publishing workflow to our product.",
author_profile_ids: [$author],
category_ids: [$category],
tag_ids: [$tag]
}')")
POST_ID=$(printf '%s' "$POST_JSON" | jq -r '.id')
POST_VERSION=$(printf '%s' "$POST_JSON" | jq -r '.version')
printf 'Draft created: %s\n' "$POST_ID"Publish after review.
curl -sS "https://api.cli-blog.com/v1/posts/$POST_ID" \
-H "content-type: application/json" \
-H "x-api-key: $CLI_BLOG_API_KEY" \
-d "$(jq -n --argjson version "$POST_VERSION" '{ status: "published", expected_version: $version }')"Use the Node SDK
Install the Node SDK in trusted server code.
npm install @cli-blog/nodeimport { CliBlog } from "@cli-blog/node";
const blog = new CliBlog({
apiKey: process.env.CLI_BLOG_API_KEY!,
});
const draft = await blog.posts.create({
title: "Shipping an API-first blog",
body_markdown: "Start with the API, then add your publishing workflow.",
author_profile_ids: ["author_123"],
});
await blog.posts.publish(draft.id, {
expected_version: draft.version,
});Show published posts in React
Use a public key in the frontend and keep private publishing keys on the server.
import { useEffect, useState } from "react";
export function BlogIndex() {
const [posts, setPosts] = useState<Array<{ id: string; slug: string; title: string }>>([]);
useEffect(() => {
fetch("https://api.cli-blog.com/v1/posts?status=published&fields=summary", {
headers: { "x-api-key": import.meta.env.PUBLIC_CLI_BLOG_KEY },
})
.then((response) => response.json())
.then((result) => setPosts(result.data));
}, []);
return <ul>{posts.map((post) => <li key={post.id}>{post.title}</li>)}</ul>;
}blog.posts.publish() is a convenience helper. This direct status update is equivalent:
await blog.posts.update(draft.id, {
status: "published",
expected_version: draft.version,
});Work with an agent
Add the skill with one command and keep the key in the trusted agent runtime.
npx skills add https://github.com/cli-blog/cli-blog-skill --skill cli-blogReview it on skills.sh, or inspect the source and other installation options in the GitHub repository.
Then ask for a draft-first flow:
Use the Cli Blog skill. Create a draft post, return the preview details, and wait for approval before publishing.