Rust Session Protocol — 41 commands
The full list of document session commands: the parameters, preconditions, and result of each command — 41 in total.
| Field | Commands | What it accepts |
|---|---|---|
tracked_change_markup_mode |
print, print-delta |
in-line — changes are shown inline in the text, the default value; balloon — as margin callouts. The mode applies to how the scene is displayed, not to the document itself. |
page_indexes |
print-window |
The numbers of the pages whose scenes to build. Numbering starts at zero. Without the field, the window is empty — the core doesn’t reject such a request. |
defer_raster_bytes |
print-window |
Don’t embed raster bytes in the response, leaving their identifiers and geometry — for the first display of the document. |
soft_page_cursor |
print-window |
An opaque cursor issued by the core — for the next window of soft pages. |
co_editing |
apply-text-transaction |
Editing during co-editing: undo and redo without a command for peers refuse before acceptance. |
rebase_package_baseline |
save |
Accept the current projection as the baseline of the session package without publishing the file. |
include_page_stack |
print-window |
Add page geometry and brief navigation data to the response. Needed when the document is first opened. |
known_fingerprints |
print-delta |
Scene fingerprints the client already has, by page number. The core returns only what changed. |
id |
all | A request tag chosen by the client. Returned unchanged — it’s what matches the response to the request. |
| ::: |
| cmd | mut. | params | precondition | result |
|---|---|---|---|---|
| apply-composition | yes | request: JSON (required) | open | CompositionReceipt |
| apply-text-transaction | yes | request: JSON (required); co_editing: boolean (optional, default false) | open | AcceptedEditTerminal { dirty, receipt, model_delta, model_projection_patch, owner_state, typing_attributes } |
| commit-snapshot | yes | document_id: string (required); path: string (required); transaction_order: integer (required); transition_kind: string (required) | open | a deprecated commit-and-history receipt |
| commit-structural-snapshot | yes | path: string (required); request: JSON (required) | open | the result of a structural edit, with a receipt |
| compatibility-inspect | no | — | open | fastdoc.docx-compatibility-command.v1 |
| compose | no | — | open | ParagraphCompositionSnapshot[] |
| model | no | — | open | CanonicalDocument |
| mutate | yes | after_caret_slot_index: integer or null (optional); before_caret_slot_index: integer or null (optional); before_selection_end_slot_index: integer or null (optional); before_selection_start_slot_index: integer or null (optional); delete_count: integer (optional, default 0); document_id: string or null (optional); offset: integer (optional, default 0); operation_kind: string (required); paragraph_index: integer (required); revision_author: string or null (optional); revision_date: string or null (optional); revision_date_utc: string or null (optional); text: string or null (optional); transaction_order: integer (optional, default 0); transition_kind: string or null (optional) | open | { mutation: BodyTextMutationSnapshot, mutation_count, history } |
| open | yes | document_id: string or null (optional); path: string (required) | no | { opened, byte_count, digital_signature_present, session_id, document_id, revision, accepted_revision, dirty, history, open_stage_timings_ms } |
| paginate | no | — | open | PaginationSnapshot |
| ping | no | — | no | { pong: true } |
| no | tracked_change_markup_mode: string or null (optional) | open | PrintParitySnapshot | |
| print-delta | no | known_fingerprints: JSON array (optional); tracked_change_markup_mode: string or null (optional) | open | snapshot details, changed_pages[], and unchanged_page_indexes[] |
| print-summary | no | — | open | { page_count, line_item_count, scene_fingerprints[] } |
| print-window | no | include_page_stack: boolean (optional); page_indexes: JSON array (optional, default empty); defer_raster_bytes: boolean (optional); soft_page_cursor: JSON (optional) | open | the print window and the built pages; on request, page_stack and the window model |
| redo | yes | document_id: string or null (optional); transaction_order: integer (optional, default 0) | open | history, the transition, redone=true, and a receipt |
| report | no | — | open | SupportReport |
| review | no | — | open | ReviewSnapshot |
| save | yes | path: string or null (optional); rebase_package_baseline: boolean (optional) | open | { saved, saved_document_itself?, byte_count, history_preserved, history } |
| save-checkpoint | yes | expected_revision: integer (required); request_id: string (required); session_id: string (required); target: JSON (required) | open | fastdoc.persistent-save-checkpoint-receipt.v1 |
| set-selection | yes | request: JSON (required) | open | SelectionUpdateReceipt |
| shutdown | yes | — | no | { id, ok:true, shutdown:true } |
| simple-field-inventory | no | — | open | SimpleFieldInventorySnapshot |
| squash-history | yes | base_undo_depth: integer (required); document_id: string (required); transition_kind: string or null (optional) | open | history state |
| status | no | — | open | session state: path, cache, history, revision, the unsaved flag, and the model hash |
| style | no | — | open | StyleSnapshot |
| track-revisions-state | no | — | open | TrackRevisionsStateSnapshot |
| undo | yes | document_id: string or null (optional); transaction_order: integer (optional, default 0) | open | history, the transition, undone=true, and a receipt |
apply-composition
Section titled “apply-composition”Parameters: request: JSON (required)
Result: CompositionReceipt
{ "id": "req-1", "cmd": "apply-composition", "request": { "...": "edit types — see “Typed editing types”" }}Concurrent access. expected_revision and expected_selection_revision are required: a request sent from a stale state is rejected.
apply-text-transaction
Section titled “apply-text-transaction”Parameters: request: JSON (required); co_editing: boolean (optional, default false)
Result: AcceptedEditTerminal { dirty, receipt, model_delta, model_projection_patch, owner_state, typing_attributes }
{ "id": "req-1", "cmd": "apply-text-transaction", "request": { "...": "edit types — see “Typed editing types”" }}Concurrent access. expected_revision and expected_selection_revision are required: a request sent from a stale state is rejected.
commit-snapshot
Section titled “commit-snapshot”Parameters: document_id: string (required); path: string (required); transaction_order: integer (required); transition_kind: string (required)
Result: a deprecated commit-and-history receipt
{ "id": "req-1", "cmd": "commit-snapshot", "document_id": "document_id-1", "path": "/absolute/document.docx", "transaction_order": 1, "transition_kind": "transition_kind-1"}commit-structural-snapshot
Section titled “commit-structural-snapshot”Parameters: path: string (required); request: JSON (required)
Result: the result of a structural edit, with a receipt
{ "id": "req-1", "cmd": "commit-structural-snapshot", "path": "/absolute/document.docx", "request": { "...": "edit types — see “Typed editing types”" }}compatibility-inspect
Section titled “compatibility-inspect”Parameters: —
Result: fastdoc.docx-compatibility-command.v1
{ "id": "req-1", "cmd": "compatibility-inspect"}compose
Section titled “compose”Parameters: —
Result: ParagraphCompositionSnapshot[]
{ "id": "req-1", "cmd": "compose"}Parameters: —
Result: CanonicalDocument
{ "id": "req-1", "cmd": "model"}mutate
Section titled “mutate”Parameters: after_caret_slot_index: integer or null (optional); before_caret_slot_index: integer or null (optional); before_selection_end_slot_index: integer or null (optional); before_selection_start_slot_index: integer or null (optional); delete_count: integer (optional, default 0); document_id: string or null (optional); offset: integer (optional, default 0); operation_kind: string (required); paragraph_index: integer (required); revision_author: string or null (optional); revision_date: string or null (optional); revision_date_utc: string or null (optional); text: string or null (optional); transaction_order: integer (optional, default 0); transition_kind: string or null (optional)
Result: { mutation: BodyTextMutationSnapshot, mutation_count, history }
{ "id": "req-1", "cmd": "mutate", "operation_kind": "insert-text", "paragraph_index": 0}Units. For mutate, offset and delete_count are counted in characters (Unicode scalar values) — not bytes and not UTF-16 units. In JavaScript, that’s what Array.from(text).length gives you; the familiar string.length returns a different number, because it counts UTF-16 units.
The second editing path — typed transactions — measures positions in UTF-16 units instead, the same way string.length does. For Latin and Cyrillic text the numbers match; for emoji and rare CJK characters they don’t: one such character is 1 for mutate and 2 for typed editing. When you switch from one path to the other, recalculate the offsets.
Parameters: document_id: string or null (optional); path: string (required)
Result: { opened, byte_count, digital_signature_present, session_id, document_id, revision, accepted_revision, dirty, history, open_stage_timings_ms }
{ "id": "req-1", "cmd": "open", "path": "/absolute/document.docx"}paginate
Section titled “paginate”Parameters: —
Result: PaginationSnapshot
{ "id": "req-1", "cmd": "paginate"}Parameters: —
Result: { pong: true }
{ "id": "req-1", "cmd": "ping"}Parameters: tracked_change_markup_mode: string or null (optional)
Result: PrintParitySnapshot
{ "id": "req-1", "cmd": "print"}print-delta
Section titled “print-delta”Parameters: known_fingerprints: JSON array (optional); tracked_change_markup_mode: string or null (optional)
Result: snapshot details, changed_pages[], and unchanged_page_indexes[]
{ "id": "req-1", "cmd": "print-delta"}print-summary
Section titled “print-summary”Parameters: —
Result: { page_count, line_item_count, scene_fingerprints[] }
{ "id": "req-1", "cmd": "print-summary"}print-window
Section titled “print-window”Parameters: include_page_stack: boolean (optional); page_indexes: JSON array (optional, default empty); defer_raster_bytes: boolean (optional); soft_page_cursor: JSON (optional)
Result: the print window and the built pages; on request, page_stack and the window model
{ "id": "req-1", "cmd": "print-window", "page_indexes": [ 0 ]}Parameters: document_id: string or null (optional); transaction_order: integer (optional, default 0)
Result: history, the transition, redone=true, and a receipt
{ "id": "req-1", "cmd": "redo"}report
Section titled “report”Parameters: —
Result: SupportReport
{ "id": "req-1", "cmd": "report"}review
Section titled “review”Parameters: —
Result: ReviewSnapshot
{ "id": "req-1", "cmd": "review"}Parameters: path: string or null (optional); rebase_package_baseline: boolean (optional)
Result: { saved, saved_document_itself?, byte_count, history_preserved, history }
{ "id": "req-1", "cmd": "save"}save-checkpoint
Section titled “save-checkpoint”Parameters: expected_revision: integer (required); request_id: string (required); session_id: string (required); target: JSON (required)
Result: fastdoc.persistent-save-checkpoint-receipt.v1
{ "id": "req-1", "cmd": "save-checkpoint", "expected_revision": 1, "request_id": "request_id-1", "session_id": "session_id-1", "target": { "path": "/absolute/document.docx", "expected_hash": "sha256:..." }}set-selection
Section titled “set-selection”Parameters: request: JSON (required)
Result: SelectionUpdateReceipt
{ "id": "req-1", "cmd": "set-selection", "request": { "...": "edit types — see “Typed editing types”" }}Concurrent access. expected_revision and expected_selection_revision are required: a request sent from a stale state is rejected.
shutdown
Section titled “shutdown”Parameters: —
Result: { id, ok:true, shutdown:true }
{ "id": "req-1", "cmd": "shutdown"}simple-field-inventory
Section titled “simple-field-inventory”Parameters: —
Result: SimpleFieldInventorySnapshot
{ "id": "req-1", "cmd": "simple-field-inventory"}squash-history
Section titled “squash-history”Parameters: base_undo_depth: integer (required); document_id: string (required); transition_kind: string or null (optional)
Result: history state
{ "id": "req-1", "cmd": "squash-history", "base_undo_depth": 1, "document_id": "document_id-1"}status
Section titled “status”Parameters: —
Result: session state: path, cache, history, revision, the unsaved flag, and the model hash
{ "id": "req-1", "cmd": "status"}Parameters: —
Result: StyleSnapshot
{ "id": "req-1", "cmd": "style"}track-revisions-state
Section titled “track-revisions-state”Parameters: —
Result: TrackRevisionsStateSnapshot
{ "id": "req-1", "cmd": "track-revisions-state"}Parameters: document_id: string or null (optional); transaction_order: integer (optional, default 0)
Result: history, the transition, undone=true, and a receipt
{ "id": "req-1", "cmd": "undo"}Commands added later
Section titled “Commands added later”The same session accepts these commands, but they didn’t make it into the original table. All of them require an open document.
| cmd | mut. | params | what it does | result |
|---|---|---|---|---|
| propose | no | request: JSON (required) | The same edit as apply-text-transaction, but on a throwaway copy of the session: the document doesn’t change |
{ proposal_id, base_revision, after_revision, request_id, applied: false, change { range_before, deleted_text, inserted_text, before_text, after_text, before_hash, after_hash } } |
| commit-proposal | yes | proposal_id: string (required) | Applies a proposal by its number; the revision is checked again | { proposal_id, committed, repeated, base_revision, accepted_revision, change, applied } |
| snapshot-checkpoint | no | expected_revision: integer (required); request_id: string (required); session_id: string (required); target: string (required) | Writes the current state to a new file target without clearing the unsaved flag |
details of the written file |
| vba-inspect | no | include_source: boolean (optional, default false) | The macro project of the open document: modules, procedures, and with include_source the source text |
fastdoc.vba.project.v1 |
| vba-plan | no | entrypoint: string (required); allow: boolean (optional); now_declared: boolean (optional); answer_declared: boolean (optional); granted_classes: JSON array (optional) | The plan for running one entry point: what will stop it — before anything is started | fastdoc.vba.plan.v1 |
| vba-preview | no | entrypoint: string (required); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional) | The macro runs against a snapshot of the session document; the diff goes out, the document doesn’t change | fastdoc.vba.preview.v1 |
| vba-trust | no | registry: string (required); action: string (optional, default ask); label: string or null (optional) |
The trust decision for the project: ask, trust, block, forget. Bound to the project hash and remembered in the registry file |
fastdoc.vba.trust.v1 |
| vba-commit | yes | entrypoint: string (required); registry: string or null (optional); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional) | The macro’s edits enter the open document by the same road as mutate. A refusal recorded in registry outranks allow |
fastdoc.vba.commit.v1 |
| vba-document-event | no | event: string (required); registry: string or null (optional); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional) | A document event from the shell — open, close, or new; reports what happened, doesn’t commit edits |
fastdoc.vba.document-event.v1 |
| apply-concurrent-text-transaction | yes | request: JSON (required); site: string (required); local_site: string (required); order: JSON (optional); typing_attributes: JSON (optional); run_format: JSON (optional) | A peer’s edit composed against an old revision: it is transferred onto the current state rather than rejected | Edit confirmation (AcceptedEditTerminal) with a transfer block: the numbers seq, base, seen, own, site, local_site, through_earlier, through_later, insert_at, and the journal log |
| note-operation-order | no | request_id: string (required); seq: integer (required) | The manager has named the number of its own, already applied edit in the shared order | The fields known, outcome (known, unknown, or out_of_order), and the journal log |
| note-operation-baseline | no | seq: integer (required) | Up to which number the file the document was opened from already contains other people’s work | { baseline } |
| note-tracked-revision-range | no | participant: integer (required) | The band of tracked-change numbers for a co-editing participant; 0 removes the band |
{ participant, range { start, end } or null, next } |
apply-concurrent-text-transaction, note-operation-order, note-operation-baseline, and note-tracked-revision-range are sent by the web host’s co-editing manager, not by your wrapper: they carry edit numbers from the shared order, which a single session doesn’t have. How this works for the user — The editor in the browser.
propose
Section titled “propose”Parameters: request: JSON (required)
Result: a preview of the edit — proposal_id, the revisions before and after, change with the text before and after and hashes, applied: false
{ "id": "req-1", "cmd": "propose", "request": { "request_id": "edit-1", "expected_revision": 3, "…": "same as apply-text-transaction" }}commit-proposal
Section titled “commit-proposal”Parameters: proposal_id: string (required)
Result: committed, repeated, base_revision, accepted_revision, change, applied. If the document has moved since propose, the refusal is proposal-stale and nothing is applied. A repeated call with the same number returns the same response with repeated: true, not a second effect.
{ "id": "req-2", "cmd": "commit-proposal", "proposal_id": "proposal-1" }snapshot-checkpoint
Section titled “snapshot-checkpoint”Parameters: expected_revision: integer (required); request_id: string (required); session_id: string (required); target: string (required)
Result: details of the written file. Unlike save-checkpoint, the person’s file, the “modified” flag, and the document address don’t change: target must be a new path, and the session must hold an unsaved edit.
{ "id": "req-3", "cmd": "snapshot-checkpoint", "session_id": "session_id-1", "request_id": "request_id-1", "expected_revision": 4, "target": "/absolute/snapshot.docx"}vba-inspect
Section titled “vba-inspect”Parameters: include_source: boolean (optional, default false)
Result: fastdoc.vba.project.v1 — macros_present, project_name, modules with procedures, offered_macros, user_forms. Read-only: the project is taken from the already open session package; the file isn’t read a second time.
{ "id": "req-4", "cmd": "vba-inspect", "include_source": true }vba-plan
Section titled “vba-plan”Parameters: entrypoint: string (required); allow: boolean (optional); now_declared: boolean (optional); answer_declared: boolean (optional); granted_classes: JSON array (optional)
Result: fastdoc.vba.plan.v1 — whether this run will go ahead and what exactly will stop it. Executes nothing and writes nothing; the plan describes a specific run, so it repeats the person’s decisions: whether the project identity is confirmed, whether the run moment and the answer to dialogs are given, and which classes of external objects are allowed.
{ "id": "req-5", "cmd": "vba-plan", "entrypoint": "Module1.FillContract", "allow": true, "now_declared": true, "granted_classes": ["Scripting.FileSystemObject"]}vba-preview
Section titled “vba-preview”Parameters: entrypoint: string (required); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional)
Result: fastdoc.vba.preview.v1 — entrypoint, result, denied_capability, stop, and the “before → after” diff. The macro does run — otherwise the preview would promise something other than what will happen — but the session document doesn’t change by a single byte: the run goes against a snapshot. The appearance of the cancel_file file stops the run at the nearest command boundary.
{ "id": "req-6", "cmd": "vba-preview", "entrypoint": "Module1.FillContract", "allow": true, "now": "2026-09-21T10:00:00", "answer": "ok", "grants": ["object:Scripting.FileSystemObject"]}vba-trust
Section titled “vba-trust”Parameters: registry: string (required); action: string (optional, default ask); label: string or null (optional)
Result: fastdoc.vba.trust.v1 — what is recorded about the open document’s project. The decision is bound to the project bytes (the hash of the vbaProject.bin part), not to the file name: editing a macro revokes trust by itself. ask only shows the state; trust, block, and forget change it. Where to store the registry is up to your application.
{ "id": "req-7", "cmd": "vba-trust", "registry": "/absolute/macro-trust.json", "action": "trust", "label": "Contract template, fill-in macro"}vba-commit
Section titled “vba-commit”Parameters: entrypoint: string (required); registry: string or null (optional); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional)
Result: fastdoc.vba.commit.v1 — entrypoint, result, and what entered the document. The edits take the same road as mutate: the document after the macro matches the document after the same edits made by hand. The policy decides whether to start: a refusal recorded in registry outranks allow. A canceled run isn’t committed at all.
{ "id": "req-8", "cmd": "vba-commit", "entrypoint": "Module1.FillContract", "registry": "/absolute/macro-trust.json", "now": "2026-09-21T10:00:00"}vba-document-event
Section titled “vba-document-event”Parameters: event: string (required); registry: string or null (optional); allow: boolean (optional); now: string or null (optional); answer: string or null (optional); grants: JSON array (optional); cancel_file: string or null (optional)
Result: fastdoc.vba.document-event.v1 — what happened on the open, close, or new event. The event is raised by the shell, not by a flag: the “run or not” decision stays with the guard and the trust policy. Edits aren’t committed — that’s what vba-commit is for.
{ "id": "req-9", "cmd": "vba-document-event", "event": "open", "registry": "/absolute/macro-trust.json" }apply-concurrent-text-transaction
Section titled “apply-concurrent-text-transaction”Parameters: request: JSON (required); site: string (required); local_site: string (required); order: JSON (optional); typing_attributes: JSON (optional); run_format: JSON (optional)
Result: AcceptedEditTerminal with a transfer field — how the edit landed after the transfer. request is the same EditRequest as for apply-text-transaction, but expected_revision may be old: a peer’s edit is transferred onto the current state. order is { seq, base }: the edit’s number in the manager’s shared order and up to which number its author had absorbed everything; typing_attributes and run_format are what the author executed the edit with, so that here it lands with the same look.
{ "id": "req-10", "cmd": "apply-concurrent-text-transaction", "site": "boris", "local_site": "anna", "order": { "seq": 17, "base": 15 }, "request": { "request_id": "edit-17", "expected_revision": 15, "…": "same as apply-text-transaction" }}note-operation-order
Section titled “note-operation-order”Parameters: request_id: string (required); seq: integer (required)
Result: known, outcome (known, unknown, or out_of_order), journal. Your own edit, already applied by apply-text-transaction, receives its number in the manager’s shared order.
{ "id": "req-11", "cmd": "note-operation-order", "request_id": "edit-16", "seq": 16 }note-operation-baseline
Section titled “note-operation-baseline”Parameters: seq: integer (required)
Result: baseline. Reports up to which number the file the document was opened from already contains other people’s work — for whoever arrives later.
{ "id": "req-12", "cmd": "note-operation-baseline", "seq": 15 }note-tracked-revision-range
Section titled “note-tracked-revision-range”Parameters: participant: integer (required)
Result: participant, range { start, end } or null, next. The core mints the numbers of its tracked changes within the participant’s band so that different people’s numbers in one document don’t collide; 0 removes the band.
{ "id": "req-13", "cmd": "note-tracked-revision-range", "participant": 2 }What comes back in the model response
Section titled “What comes back in the model response”The CanonicalDocument type is how the model command answers the question “what’s in the document right now”. It’s built from two layers, and that’s its main property.
Counters — how much of what is in the document: block_count, paragraph_count, run_count, section_count, table_count, table_row_count, table_cell_count, body_word_count, header_count, footer_count, footnote_count, endnote_count, field_count, comment_count, tracked_change_count, object_count, style_definition_count, numbering_definition_count, support_marker_count. You use them to check that the file opened as expected, without parsing the content.
Content — the objects themselves: body with text blocks, sections, headers, footers, footnotes, endnotes, fields, comments, tracked_changes, objects, style_definitions, numbering_definitions, support_markers. Plus document_design.
Four fields appear only when there’s something to show: document_protection, bibliography, footnote_properties, and endnote_properties.
model_version, stage_id, stage_name, and word_count_source stand apart: the model version, the parsing stage label, and where the word count was taken from. The document language is document_language: read from the first paragraph with text, then from the defaults layer, then from the most frequent paragraph language; the defaults layer itself (document_default_language) is stored in the model but doesn’t appear in the response.
Names like PaginationSnapshot, StyleSnapshot, ReviewSnapshot, or AcceptedEditTerminal won’t turn up in the type catalog: the catalog is built from the TypeScript shell contract, while these are declared in the Rust core. The easiest way to see what each one contains is in the response itself — run the command on your document and look at the JSON: the fields are named the same as in this breakdown.
What comes back after an edit
Section titled “What comes back after an edit”AcceptedEditTerminal is the response to mutate and apply-text-transaction. It answers the question that comes up right after a successful edit: what to show the user without rereading the whole document.
| Field | What it is |
|---|---|
receipt |
A receipt for the edit: before_revision and after_revision, the selection revision numbers before and after, selection_after itself, the history state, and the boundary of what changed. You use it to confirm that it was your change that got applied. |
model_projection_patch |
What to redraw: story_id, node_id, the block number in the body, and paragraph_after itself — the paragraph after the edit. Plus before_hash and after_hash, which show which state the patch was built from. |
model_delta |
What exactly changed in the text: the range before the edit, the deleted and inserted strings, the text before and after. Useful for a log and for replaying the edit on your side. |
owner_state |
The session owner’s state at the moment of the response. |
typing_attributes |
The character properties at the input point: what the next text you type will look like. |
dirty |
Whether the document has unsaved changes. |
projection_baseline |
Only for documents in restricted mode, and only in the response to the first edit after activation: a pair of hashes, owner_hash (what the shell held before activation) and model_hash (the model built from the same package). From it, the shell understands that two different hashes describe the same document. The field is absent from other responses. |
The rule is simple: render from model_projection_patch — it gives you a ready-made paragraph and where to put it. model_delta is for when you need to record or replay the edit on your side, not display it.
Layout and review snapshots
Section titled “Layout and review snapshots”PaginationSnapshot (the response to paginate) and ReviewSnapshot (the response to review) are built the same way as the model response: counters first, then content. If you’ve made sense of one, you’ll make sense of the others.
PaginationSnapshot — how the document breaks down into pages. Counters: section_count, explicit_break_count, rendered_break_observation_count, page_fragment_count, hard_split_count, table_placeholder_count, anchored_object_placeholder_count, support_marker_count. Content: sections, explicit_breaks, page_fragments, support_markers.
Separately, three spread flags — even_and_odd_headers_enabled, mirror_margins_enabled, gutter_at_top_enabled: different headers and footers for even and odd pages, mirrored margins, and a gutter at the top. Plus frontier_mode — how far layout has been computed.
ReviewSnapshot — comments and tracked changes. Counters: comment_count, tracked_change_count, resolved_comment_anchor_count, deferred_comment_anchor_count, support_marker_count. Content: comments, tracked_changes, support_markers. The snapshot_mode field says in what form the snapshot was taken.
Two anchor counters are worth telling apart: resolved_comment_anchor_count — comments whose anchor to the text was successfully restored; deferred_comment_anchor_count — those whose anchor is deferred. A nonzero second number means some comments currently have nothing to attach to for display.
Like model, both snapshots have model_version, stage_id, and stage_name — the model version and the parsing-stage marker.