---
name: edit-post
description: Edit a Bloggo post together with the person, block by block, without sending the whole text again. Use when asked to change, reword, move, add or remove parts of a post, undo a change, or let the person watch an edit before it goes live.
---

# Edit a post by numbered blocks

Use this instead of `PATCH /posts/:id` when the person wants to follow and steer an edit. You send only the blocks that change; the person watches a live page where every block has a number, says "rewrite 7" or "move 12 above 9", and approves at the end. This needs the edit mode feature (`GET /features` shows `post_edit_mode`); without it every call answers `403 feature_not_available`, so use `PATCH`. Setup, keys and errors are in the [Bloggo skill](https://skill.bloggo.blog/posts/bloggo-skill/); this one only covers editing.

## Start

`POST /posts/:id/edit` (any status, drafts too). The reply has `edit.url`, `edit.revision` and `edit.blocks`: every block with its number `n` and its `markdown`. Block 1 is the title, block 2 is the frontmatter if the post has one, then each paragraph, heading, whole list, code block, quote or table is one block, and an image with its italic caption line is one block. Give the person `edit.url` and say they can watch the changes there; the link only lets them look. Calling it again on an open edit returns the same work with a new link and the old link stops working.

The post itself does not change until you approve. Numbers are positions: they shift when blocks are inserted or deleted, and they are not part of the post.

## Change blocks

`POST /posts/:id/edit/commands` with `{"revision": N, "commands": [...]}`, `revision` taken from your last reply. Commands apply in order, each seeing the result of the one before; if one fails nothing is applied.

- Replace a block: `{"op":"edit","block":7,"markdown":"New text"}`. Block 1 takes the new title.
- Insert: `{"op":"insert","after":7,"markdown":"..."}` (between 7 and 8), or `"at":"start"` or `"at":"end"`. The text can be several blocks.
- Delete: `{"op":"delete","block":7}` or `"block":"7-9"`.
- Swap two blocks or two ranges: `{"op":"swap","a":7,"b":8}`. Move block 7 down: swap 7 and 8. Move block 7 up: swap 6 and 7. Move block 12 above blocks 9 to 11: `{"op":"swap","a":"9-11","b":12}`.

The reply has `changed` (the touched blocks with their new numbers) and the new `revision`. Send only the blocks you change. If the numbers below an insert or delete moved, use the new ones. A `409 stale_edit` means the numbers are out of date (the person or another caller undid something): `GET /posts/:id/edit` shows the current blocks and revision; look again before you resend. A `422` says why a command was refused; the title and frontmatter cannot be moved or deleted, and two lists in a row would merge into one block, so keep a paragraph between them.

## Undo and redo

When the person says "undo that", send `{"op":"undo"}` as the only command in a call (`"steps": 3` goes back three). `{"op":"redo"}` goes forward. Every step is kept as a tree: a new change after an undo starts another branch and the undone one stays. `GET /posts/:id/edit/history` lists the steps (`step`, `parent`, `summary`, `current`), and `{"op":"undo","to":4}` jumps to step 4 on any branch, so "go back to the version before I asked for the shorter intro" is a lookup in the history and one call. Tell the person which step you are on.

## Approve

Only when the person says it looks right, call `POST /posts/:id/edit/approve`. It saves the post with the normal checks (size limit, content check) and publishes it, so a draft goes live; a published post is updated. If the post was changed another way after the edit started you get `409 post_changed`: tell the person, and only send `{"force": true}` if they agree to replace that change. To drop the edit, `DELETE /posts/:id/edit`.

An edit that nobody changes for 24 hours expires. A blog can have 10 posts in edit at once.
