Publish the blog.
Keep the API clean.
Write posts, upload images, generate a key. Your app fetches the content — nothing else ships with it.
GET /api/v1/projects/blog/posts
[
{
"id": "clv4x...",
"title": "Launch notes — May 2025",
"slug": "launch-notes-may-2025",
"content": "Markdown body...",
"thumbnail": "https://res.cloudinary.com/...",
"tags": ["product", "updates"],
"publishedAt": "2025-05-12T09:00:00Z"
}
]An editor that does not fight you.
Write in Markdown. Attach a thumbnail. Add tags. Publish or save as draft. That is the whole editor — no block widgets, no page builder config, no CMS settings to survive.
Images go to Cloudinary. Content goes to the API. Your frontend gets clean structured data it can actually use.
One account, separate projects
Keep a product blog, a changelog, and a client site under one login. Each project has its own API endpoint and its own keys.
Scoped API keys
Issue a content:read key for your frontend and a content:write key for automation. Revoke either without touching the other.
No page-builder payload
The response is flat: title, slug, content, thumbnail, tags, author. Your app uses it directly.
For developers
The content endpoint is the product.
Keep editors in LaughingWaffle. Keep your app in your codebase. One authenticated GET returns everything you need — no GraphQL schema, no client SDK.
fetch("https://api.laughingwaffle.online/api/v1/projects/blog/posts", {
headers: { Authorization: "Bearer lw_live_..." }
})
.then(r => r.json())
.then(posts => {
// flat array — use it however you want
})If it feels too heavy, it is not doing its job.
No card required. One real post is enough to know.