---
title: "Undo a revert: restore the pre-revert state and the rolled-back turns."
method: POST
path: "/v1/sessions/{session_id}/redo"
tags: ["v1", "mcp", "sessions"]
---

# Undo a revert: restore the pre-revert state and the rolled-back turns.

`POST /v1/sessions/{session_id}/redo`

## Request

- Base URL: `https://api.superdocs.app`
- URL: `https://api.superdocs.app/v1/sessions/{session_id}/redo`
- Auth: HTTP bearer

Revert is non-destructive: this restores the session FORWARD to the pre-revert state
(returned by the revert as redo_checkpoint_id), restores the document to that state while still
merging any concurrent edits from other sessions (keeping both where they conflict), and
un-archives exactly the chat turns the revert hid. Intended for use immediately after a revert;
once a new message is sent the timeline diverges and the web app stops offering it.

## Path parameters

- `session_id` string, required

## Headers

- `authorization` string, nullable

## Request body

- RedoRequest: Undo a revert — restore the captured pre-revert state and the rolled-back conversation + document. Meant for use immediately after a revert (before sending a new message, which would diverge the timeline); the web app gates it to that window.
  - `turn_index` integer, required: The turn the revert rewound to (rows at/after this were archived); redo un-archives exactly those.
  - `redo_checkpoint_id` string, required: The identifier of the pre-revert state, returned by the revert response.
  - `window_id` string, nullable: Optional identifier for the client window issuing the request, used to coordinate concurrent reverts from multiple browser tabs.

## Response `200`

Successful Response

- RevertResponse: Result of a session revert: restored document state and compose-box prefill.
  - `session_id` string, required: Session identifier.
  - `reverted_to_turn` integer, required: Turn index that the active conversation now ends at (the AI reply preceding the reverted user message). -1 when the session is reset to its initial empty state (revert from the very first user message).
  - `compose_text` string, required: The text of the user message that was reverted. Clients should pre-fill the compose box with this so the user can edit and resend.
  - `document_state` DocumentState: Current document state for session restoration.
    - `html_content` string, nullable: Full document HTML content with data-chunk-id attributes.
    - `document_id` string, nullable: Session-local id (slug) of the focused document. Identifies the document on restore so a restored tab stays in sync with edits made to it in other sessions.
    - `version_id` string, nullable: Document version identifier (UUID string).
    - `chunk_count` integer: Number of content sections in the document.
    - `last_modified` string, nullable: ISO 8601 timestamp of the last modification.
    - `attachments` object[], nullable: Session attachments. Each item has: id (string), filename (string), file_extension (string), processing_status ('ready', 'processing', or 'failed').
    - `page_setup` object, nullable: Source page geometry of the focused document (width_in, height_in, margin_in{top,right,bottom,left}, orientation) when known, else null. The editor renders the page at this geometry on restore; null ⇒ US-Letter + 1in default.
  - `editor_action` string: Client instruction: 'update' to load the restored document, or 'clear' to reset the editor to empty.
  - `archived_turn_count` integer, required: Number of chat rows soft-archived by this revert (turns hidden from the UI but retained for audit).
  - `revert_changes` object, nullable: Per-document (slug -> change-set) revert diff. Applying these merges the revert onto any concurrent live edits — a conflicting section is surfaced for you to resolve rather than replacing the whole document. On dry_run this is the preview; on a real revert it is what was committed.
  - `revert_conflicts` object, nullable: Per-document (slug -> entries) sections whose restore did NOT fully land. TWO classes, told apart by the 'displaced' flag. (1) NOT LANDED ('displaced' absent/false): content another writer committed was preserved instead (reason 'concurrent_edit_preserved' / 'lineage_unknown'), or a removal could not be resolved against the live document; the matching revert_changes entry is annotated 'conflict': true and carries the preserved bytes as new_html (the honest committed state) plus the un-landed restore as restore_html. (2) LANDED IN THE WRONG PLACE ('displaced': true): the content WAS restored but not at its original position — reason 'restored_elsewhere' (it was added at the end of the document because the section it anchors to is gone) or 'position_not_restored' (a concurrent writer's placement won). These carry an empty their_html (nothing of another writer's was preserved, so there is nothing to adopt) plus the raw 'resolution' token, and the matching revert_changes entry is annotated 'displaced': true — never 'conflict', because new_html is still the restored content. (3) NOT SAVED AT ALL ('write_failed': true, operation 'write', chunk_id null): the document's recovery write did not commit — reason 'document_deleted' (archived mid-revert), 'storage_paused' (durable saving paused for this account) or 'write_failed'. Document-level, so there is no matching revert_changes entry; storage still holds the pre-revert content while document_state shows the reverted document, so a client MUST re-read the document before saving again. Null/absent when the revert landed cleanly everywhere.
  - `dry_run` boolean: Echoes the request's dry_run: when true nothing was committed/archived — this is a preview only.
  - `redo_checkpoint_id` string, nullable: The identifier of the pre-revert state. Revert is non-destructive — passing this back to POST /sessions/{id}/redo (with the same turn_index) restores it and un-archives the rolled-back turns, undoing the revert. Null on a dry-run, and null when the pre-revert state could not be captured — in that case redo_unavailable_reason says which, so absence by design and absence by failure are never the same value.
  - `redo_unavailable_reason` string, nullable: Present ONLY when redo_checkpoint_id is null because the pre-revert state could not be captured (never on a dry-run, where null is by design). The revert itself succeeded and is durable; what is unavailable is the one-step undo of it. Treat a non-null value as: do not attempt redo_revert for this revert — it has no argument to pass.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/superdocs/apis/universal-document-ai-api.md) · [All operations](https://skmtc.dev/superdocs/apis/universal-document-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/superdocs/apis/universal-document-ai-api/revisions/37d03122940b?raw)
