/api/postsCreate or upsert post
Create a draft or published post, or upsert by slug with an account API key.
Primary write endpoint for the CLI and Obsidian plugin. Project is selected via X-Blogizi-Project, body fields, or automatically when the account has exactly one project. See Authentication.
Request
POST /api/posts
Authorization: Bearer YOUR_ACCOUNT_API_KEY
X-Blogizi-Project: your-project-slug
Content-Type: application/json{
"title": "How we built our auth flow",
"slug": "how-we-built-our-auth-flow",
"description": "≤160 char meta description",
"keyword": "indie app authentication",
"content": "Markdown body…",
"status": "draft",
"scheduledAt": "2026-10-26T09:00",
"upsert": false
}Fields
title,slug— requireddescription,keyword,content— optional but recommended for SEOcoverImageUrl— optional public cover image URL (also accepted as frontmattercoverImageUrl/coverImage). On upsert, omit to keep the existing cover. Upload bytes with Upload post image first if needed.ctaTitle,ctaSubtitle,ctaButtonLabel,ctaButtonUrl— optional per-post CTA override (same rules as Write in Markdown); on upsert, omit a field to keep the existing valuesidebarCtaTitle,sidebarCtaText,sidebarCtaBullets(string array),sidebarCtaButtonLabel,sidebarCtaButtonUrl— optional per-post sidebar CTA override (title, button label, and URL required together); omit to keep existing values on updatestatus—draftorpublished(publishedpublishes immediately and cancels any schedule)scheduledAt— optional ISO datetime (or naive local interpreted in the project timezone). Future → draft + schedule;nullclears a schedule; omit leaves it unchanged on upsert. Response includesscheduledAt(UTC ISO); internal queue ids are never returnedupsert— whentrue, update an existing post with the same slug (Obsidian plugin pattern). Default create fails if the slug already exists.projectSlug/project/projectId— optional if the header or sole-project rule already selects a blog
Inline images in content
Use standard Markdown after uploading a content image:
Flow: create/upsert the post → upload with type=content → upsert again with the markdown snippet in content.
Response
Returns the post payload plus { project: { id, slug }, created: boolean }. Status is 201 on create and 200 on upsert update. When scheduled, data.status stays draft and data.scheduledAt is a UTC ISO string.
{
"data": {
"slug": "how-we-built-our-auth-flow",
"status": "draft",
"scheduledAt": "2026-10-26T13:00:00.000Z"
},
"project": { "id": "665f…", "slug": "my-app" },
"created": true
}Prefer CLI publishing for local markdown files. Use this endpoint when building your own client or when the Obsidian plugin upserts by slug.