Writing content

Create, update, publish and unpublish posts, and upload media, with the Writizzy public API.

The write side of the v1 API lets you manage your content from your own tools: publish from a Git repository, run an inbound migration script, or wire Writizzy into a custom editorial pipeline. It stays in the same /v1 contract as the read endpoints, no separate version.

Use a write key

Writing requires a write key. A read key can only read: any create, update, publish or upload request made with a read key returns 403. A write key includes read access, so a single script can fetch a post and then patch it.

Generate the write key from Blog Settings → Developer API, then pass it as a Bearer token like any other request:

POST /v1/blogs/yourblog/posts
Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The write flow

A post is always created as a draft. Publishing is a separate, explicit step. A typical flow is:

  1. POST /posts to create the draft, you get back its id and final slug
  2. optionally PATCH /posts/{id} to refine it
  3. POST /posts/{id}/publish to make it visible

The author of any post created through the API is the blog owner.

Endpoints

MethodPathDescription
POST/v1/blogs/{subdomain}/postsCreate a draft
PATCH/v1/blogs/{subdomain}/posts/{id}Update a post (partial)
POST/v1/blogs/{subdomain}/posts/{id}/publishPublish a post
POST/v1/blogs/{subdomain}/posts/{id}/unpublishRevert a post to draft
POST/v1/blogs/{subdomain}/mediaUpload a media file

Create a draft

title and content are required. slug is optional: when omitted it is generated from the title, and de-duplicated automatically if it collides with an existing post. Tags are created on the fly.

curl -X POST https://writizzy.com/v1/blogs/yourblog/posts \
  -H "Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Getting started with Kotlin",
    "content": "# Hello\n\nThis is my first post.",
    "tags": ["kotlin", "tutorial"]
  }'

The response is the full post, including the resolved slug and id. Keep them as your reference: creation is not idempotent, so a replayed request creates another post.

{
  "id": "p_abc123",
  "title": "Getting started with Kotlin",
  "slug": "getting-started-with-kotlin",
  "content": "# Hello\n\nThis is my first post.",
  "publishedAt": null,
  "accessMode": "FREE",
  "...": "..."
}

Update a post

Send only the fields you want to change, the rest are left untouched. This call never changes the publication state.

curl -X PATCH https://writizzy.com/v1/blogs/yourblog/posts/p_abc123 \
  -H "Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "excerpt": "A short intro to Kotlin." }'

Changing the slug after publication is discouraged, it breaks the public URL. A slug collision returns 400.

Publish and unpublish

Publishing makes the post visible. That is its only effect: no newsletter is sent and no cross-posting is triggered, those are separate, explicit actions. A future publishedAt schedules the post, and it defaults to today when omitted. Publishing an already-published post is a no-op.

curl -X POST https://writizzy.com/v1/blogs/yourblog/posts/p_abc123/publish \
  -H "Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Unpublishing reverts the post to a draft. It is reversible and safe, and a no-op on a draft.

curl -X POST https://writizzy.com/v1/blogs/yourblog/posts/p_abc123/unpublish \
  -H "Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Upload media

Upload an image as multipart/form-data and get back its public CDN URL. Use that URL as a post coverImageUrl or inside the Markdown content.

curl -X POST https://writizzy.com/v1/blogs/yourblog/media \
  -H "Authorization: Bearer wz_yourblog_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "file=@cover.png" \
  -F "altText=Cover image"
{
  "url": "https://cdn.writizzy.com/blogs/.../cover.png",
  "filename": "cover.png",
  "size": 48213
}

Uploads count against your plan's media storage quota. A request that would exceed it returns 403.

Notes

  • The post type is always POST. Static pages are not editable through the API.
  • The author is the blog owner, there is no third-party author to associate.
  • Hard deletion is not exposed. Use unpublish to remove a post from the public site.
  • All write endpoints return the standard error format.