---
title: "Update a blog post"
method: PATCH
path: "/blog/posts/{id}"
tags: ["Blog posts"]
---

# Update a blog post

`PATCH /blog/posts/{id}`

## Request

- Base URL: `https://api.cavuno.com/v1`
- URL: `https://api.cavuno.com/v1/blog/posts/{id}`
- Auth: HTTP bearer

Updates a blog post. A `status` value is rejected with `blog_invalid_transition` unless it is the scheduling carve-out (`status: "scheduled"` with a future `publishedAt`); use the publish/unpublish endpoints for other lifecycle changes.

## Path parameters

- `id` string, required: The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.

## Request body

- UpdateBlogPostBody
  - `slug` string
  - `html` string
  - `customExcerpt` string
  - `readingTimeMin` integer
  - `featured` boolean
  - `authorIds` string[]
  - `tagIds` string[]
  - `coverMediaId` string, nullable
  - `ogImageMediaId` string, nullable
  - `featureImageAlt` string
  - `featureImageCaption` string
  - `seoTitle` string
  - `seoDescription` string
  - `canonicalUrl` string, nullable: Canonical URL for the post. Pass `null` or `""` to clear the stored value; omitted means unchanged.
  - `publishedAt` string, date-time
  - `title` string
  - `status` 'draft' | 'scheduled' | 'published'

## Response `200`

Successful response.

- BlogPost
  - `id` string, required: Unique identifier for the object. Use this value as the `{id}` path parameter for the blog-post endpoints (e.g. `GET /v1/blog/posts/{id}`).
  - `object` 'blog_post', required
  - `title` string, required
  - `slug` string, required
  - `status` string, required
  - `visibility` string, required
  - `type` string, required
  - `featured` boolean, required
  - `authorIds` string[], required
  - `tagIds` string[], required
  - `coverUrl` string, nullable, required
  - `ogImageUrl` string, nullable, required
  - `featureImageAlt` string, nullable, required
  - `featureImageCaption` string, nullable, required
  - `customExcerpt` string, nullable, required
  - `readingTimeMin` number, nullable, required
  - `seoTitle` string, nullable, required
  - `seoDescription` string, nullable, required
  - `canonicalUrl` string, nullable, required
  - `publishedAt` string, nullable, required
  - `createdAt` string, required
  - `updatedAt` string, nullable, required
  - `html` string, nullable, required

## Other responses

- `400`: Malformed request or an invalid status transition.
- `404`: Blog post not found.
- `409`: The slug change collides with an existing post.

## Changes

- **2026-10-19** `c34152c38176`: 3 info
  - added the optional property `error/upgrade` to the response with the `400` status
  - added the optional property `error/upgrade` to the response with the `404` status
  - added the optional property `error/upgrade` to the response with the `409` status

[Operation history](https://skmtc.dev/cavuno/apis/cavuno-v1-rest-api/changes/blog/posts/:id/patch.md)

---

[API](https://skmtc.dev/cavuno/apis/cavuno-v1-rest-api.md) · [All operations](https://skmtc.dev/cavuno/apis/cavuno-v1-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cavuno/apis/cavuno-v1-rest-api/revisions/d5974dabff14?raw)
