The built-in reading pages are deliberately plain. The durable part is underneath, so you can replace the front end entirely and keep everything else.
| URL | What |
|---|---|
/blog/<slug>/feed.rss |
One blog, RSS 2.0 |
/blog/<slug>/feed.atom |
One blog, Atom |
/blog/<slug>/feed.json |
One blog, JSON Feed 1.1 |
/feed/blog.rss |
Newest posts across all public blogs |
/blog/feed.json |
The same, as JSON Feed |
Feeds follow the blog's visibility: a public blog's feed is open, a
members-only blog's feed asks for credentials (readers can't sign in, so it
returns a 401 that a reader can retry with an API token), and a private
blog's feed is not there at all. Drafts never appear in any of them.
Blog pages advertise their feeds in the <head>, so a reader app finds them
from the page URL alone.
Anonymous, no token, published content only:
GET /blog/api/blogs/
GET /blog/api/blogs/<slug>/posts/?page=2&tag=ops
GET /blog/api/blogs/<slug>/posts/<post-slug>/ # includes the body
GET /blog/api/blogs/<slug>/tags/
This is what to build a custom reader against — a React front end, a static-site generator, another service. Responses are cacheable for a minute.
For response shapes, pagination, visibility rules, CORS and worked client code — and for rendering posts inside this project's own templates — see Showing posts in your own pages.
GET|POST /smallstack/api/blog/posts/
GET|PUT|PATCH /smallstack/api/blog/posts/<id>/
…and the same for blogs, tags, issues, subscriptions
These need an API token (mint one under API Tokens) and return only what that user can see. They are the write surface — don't point a public reader UI at them; it will get a 401.
Publishing is not a field you can PATCH. Use the MCP tool below, or the console.
Connect Claude to this site and it can list, read, draft and publish:
list_blog_posts, get_blog_post, search_blog_postsblog_draft_post — write a draft from markdown, with tagsblog_publish_post / blog_unpublish_postblog_list_publishable_documents, blog_publish_runbook_document,
blog_resync_postEach tool sees exactly what the token's owner can see.
Sending a newsletter is not available as a tool, and subscriber addresses are not exposed to AI clients at all. Mailing thousands of real people stays a deliberate human action, and a subscriber list is not something to page into a chat client.
Turn on a webhook endpoint under Webhooks and subscribe to:
| Event | When |
|---|---|
smallstack_blog.post.created / .updated / .deleted |
a post changes |
smallstack_blog.blog.created / .updated |
a blog changes |
smallstack_blog.issue.updated |
an issue's status changes, including sent |
smallstack_blog.subscription.created / .updated |
someone subscribes or leaves |
There is deliberately no per-delivery event: a 5,000-recipient send would emit 10,000 webhooks and drown whatever is listening. Watch the issue instead.
Choose the color mode for your app.
The accent color for your app.
Choose the font family that fits your app.
Choose the gray shade for your app.
Choose the border radius factor for your app.
Choose the page layout for your app.