Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Admin API

All admin endpoints are protected by Bearer token authentication. Set the token via PLINTH_API_KEY.

Authorization: Bearer <your-api-key>

Publish article

POST /api/admin/articles
Content-Type: application/json

Request body (PublishArticleRequest):

FieldTypeRequiredDescription
titlestringnoArticle title (can come from frontmatter)
slugstringnoURL slug (auto-generated from title if omitted)
descriptionstringnoMeta description
contentstringyesMarkdown or Typst source content
html_contentstringnoPre-rendered HTML (required for Typst)
tagsstring[]noTag names
authorstringnoAuthor name (defaults to site author)
publishedboolnoVisibility (default: true)
featuredboolnoFeatured flag (default: false)
embeddingfloat[]no384-dim fastembed vector
content_formatstringno"Markdown" (default) or "Typst"

Response (200):

{
  "success": true,
  "slug": "my-article",
  "id": "blog_posts:abc123",
  "message": "Article 'My Article' published successfully"
}

Error (400):

{
  "error": "Title is required",
  "details": "Provide title in request or frontmatter"
}

List tags

GET /api/admin/tags

Returns all tags with metadata.

Response (200):

[
  { "id": "tags:abc", "name": "rust", "slug": "rust", "post_count": 5 }
]

Add tag to post

POST /api/admin/posts/{post_slug}/tags
Content-Type: application/json

Request body (AddTagRequest):

{ "tag": "new-tag" }

Creates the tag if it doesn’t exist, then creates a tagged graph relation.

Remove tag from post

DELETE /api/admin/posts/{post_slug}/tags/{tag_slug}

Removes the tagged graph relation. Does not delete the tag itself.

Update site content

PUT /api/admin/content/{key}
Content-Type: application/json

Request body (UpdateSiteContentRequest):

FieldTypeDescription
titlestringContent block title
contentstringMarkdown source
html_contentstringRendered HTML

Upserts the content block — deletes existing entry with the same key and creates a new one.

Get site content

GET /api/admin/content/{key}

Returns the site content block, or null if not found.