Built-in modules
Blog
Articles, categories and a resolved SEO chain, in a built-in module you configure nothing for.
A built-in module: articles and categories whose schema ZevContent defines, documents and maintains. Nothing is configured first. Every project has these endpoints, and they serve an empty list until somebody writes something.
Bylines come from the shared authors module, not from here.
List articles
/v1/blog/articles Published articles, newest first by published_at. Cursor-paginated. body is
omitted, so fetch one article for it.
| Parameter | |
|---|---|
limit | 1–100, default 20 |
cursor | meta.next_cursor from the previous page |
category | A category slug. Matches an article carrying it anywhere in categories. Unknown → error. |
author | An author slug. Matches anywhere in authors, so a co-authored post appears on both pages. Unknown → error. |
tag | An exact tag. No match → empty list. |
q | Case-insensitive substring of title or excerpt |
media_urls | permanent to refuse expiring image URLs |
q combines with category and author, which is how a category page or an
author page offers search within its own articles without a second
endpoint.
One article
/v1/blog/articles/{slug} The whole article, with body as { html, json, plain, toc }, every image
resolved, and the same toc beside the article so a sidebar table of contents
does not have to reach inside the body.
An unknown slug teaches with did_you_mean; a slug that exists but is a draft
says so (blog_article_not_published).
Categories
/v1/blog/categories /v1/blog/categories/{slug} Every category alphabetically, each with article_count, its featured_image
and its resolved seo. That is enough to build blog navigation, section cards
included, from one request.
A category page is two requests and never more: the page itself, and
GET /v1/blog/articles?category={slug} for its articles, which pages and takes
q.
The article shape
{
"id": "art_…",
"slug": "how-we-deploy",
"title": "How we deploy",
"excerpt": "A short tour of the pipeline.",
"body": { "html": "…", "json": {}, "plain": "…", "toc": [] },
"toc": [{ "level": 2, "text": "The pipeline", "id": "the-pipeline" }],
"featured_image": { "url": "…", "alt": "…", "width": 1600, "height": 900, "content_type": "image/jpeg" },
"authors": [{ "id": "aut_…", "name": "Ada", "slug": "ada", "bio": "…", "avatar": null, "social": {} }],
"categories": [{ "id": "cat_…", "name": "Engineering", "slug": "engineering", "description": null }],
"tags": ["deploys", "ci"],
"published_at": "2026-08-14T09:00:00.000Z",
"updated_at": "2026-08-20T11:12:00.000Z",
"reading_time_minutes": 6,
"word_count": 1180,
"seo": { "title": "…", "description": "…", "og_image": {}, "canonical_url": null, "noindex": false }
}
body and toc appear on one article only, never in a listing. Twenty full
rich text documents, each rendered three ways, is a response measured in
megabytes for a page that shows twenty cards.
Arrays, and the first element
authors and categories are ordered arrays, and the first element of
each is the primary one:
authors[0]is the lead byline: the name to print when there is room for one.categories[0]is the primary section: the breadcrumb, the eyebrow label, the section URL.
The order is the one a writer chose. Render it as given; never re-sort.
Both may be empty, since an unattributed post and an uncategorised one are both ordinary, but neither is ever null, so map over them without a check.
References and pages
Nested inside an article, an author and a category are references: the
byline and the breadcrumb. They carry no article_count and no seo, because
a card printing twenty bylines will never render twenty og images.
On their own endpoints they are pages and carry those as well. The category
page is /v1/blog/categories/{slug}; the author page is /v1/authors/{slug},
which is not under /v1/blog.
Categories and tags are different things
A category is a taxonomy term with a page of its own: a slug, a
description, an image, an SEO object, and a URL. A tag is a free-form label
with no page. Many per article, matched exactly by ?tag=.
If you find yourself wanting a page for a tag, it should have been a category.
SEO
Every article and category carries a complete, resolved seo object. Do not
reimplement the fallback chain. See SEO.
Notes
- Everything here is read-only and works with any key kind. Articles are written in the dashboard.
- Only published articles are served. A draft is a 404 that says it is a draft.
- A future
published_atdoes not publish an article by itself. Scheduling is not built. - The body is rich text at schema version 2. An article written before schema 2
keeps
"version": 1and renders exactly as it always did. See Rich text.
Updated at, Wednesday, September 2, 2026