Auth
Every endpoint requires a Bearer token.
Authorization: Bearer <your_api_key>
Base URL: https://blog.ibuildathing.comEndpoints
GET /api/posts
List posts. Optional filters: ?author=chinat|xisen, ?published=true|false, ?limit=N. Returns { count, posts }.
GET /api/posts?slug=<slug> or GET /api/posts/<slug>
Fetch one post. 404 if missing.
POST /api/posts
Create or upsert. Required: slug, title, author, session_date, content_md. Optional: excerpt, published. Same slug posted twice updates in place.
PATCH /api/posts/<slug> or PUT /api/posts/<slug>
Partial update. Only fields you pass are changed.
DELETE /api/posts/<slug>
Delete by slug. 404 if already gone.
Schema
type Author = 'chinat' | 'xisen';
type Post = {
id: string; // uuid
slug: string; // url-safe, unique
title: string;
author: Author;
session_date: string; // YYYY-MM-DD (mastermind session)
content_md: string; // markdown body
excerpt: string | null;
published: boolean;
created_at: string;
updated_at: string;
};Quickstart
# list all posts
curl -H "Authorization: Bearer $API_KEY" \
https://blog.ibuildathing.com/api/posts
# create a draft
curl -X POST -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"slug": "2026-04-19-xisen-pulse-security",
"title": "What Pulse leaks when it works",
"author": "xisen",
"session_date": "2026-04-19",
"content_md": "# body\n\n...",
"excerpt": "One-liner hook.",
"published": false
}' \
https://blog.ibuildathing.com/api/posts
# publish later
curl -X PATCH -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"published": true}' \
https://blog.ibuildathing.com/api/posts/2026-04-19-xisen-pulse-security
# delete
curl -X DELETE -H "Authorization: Bearer $API_KEY" \
https://blog.ibuildathing.com/api/posts/2026-04-19-xisen-pulse-securityErrors
401— missing Authorization header403— invalid API key400— invalid JSON, missing required fields, bad author404— post not found
Notes
- Slug convention:
YYYY-MM-DD-<author>-<short-kebab>. published: falsehides from/but stays readable by slug in the admin UI.- CRUD is idempotent where possible: POST with an existing slug updates; PATCH with unchanged fields no-ops.
- Repo: cyu60/blog-ibuildathing
- Integration tests:
scripts/test-api.sh(20 assertions, green on prod).