Skip to content

Edit operations

Every operation the edit API accepts: locators, text selectors, content runs, property values, how a request is applied, and each operation's fields, values, examples and refusals.

View as Markdown

This is the complete reference for the edits array of POST /v1/edit. Each edit is a JSON object with a type and the fields that type defines. Unknown fields are refused, and edits are applied in request order. The full machine-readable definition of each edit is in the API's OpenAPI specification under components.schemas.EditRequest.

Edits name the things they act on by locators, and the locators come from the document content: the response of GET /v1/files/{file_id}/content?format=json. Read that first and copy the locators it prints instead of computing them. How a request is applied says how several edits in one request combine.

Operations at a glance

OperationChangesNamesTracked
insertnew text, breaks, fields, cross-references, notes and links at a pointparagraphs and a pointyes; a cross-reference's bookmark, a new notes part and note styles are not
replace, deletetext, or a page breakparagraphs and a selectoryes
formatcharacter formattingparagraphs, and optionally a selectoryes
insert_paragraphsnew paragraphsa destinationyes; a new list's definitions are not
delete_paragraphswhole paragraphs, and tables a range coversparagraphsyes
move_paragraphswhere paragraphs and tables areparagraphs and a destinationyes
split_paragraphone paragraph into twoa paragraph and a pointyes
join_paragraphsa range of paragraphs into onea rangeyes
set_paragraph_styleparagraph styleparagraphsyes
set_paragraph_formatalignment, indents, spacing, paginationparagraphsyes
set_numberinglist numberingparagraphsyes; new list instances and definitions are not
insert_section_breaka new section breakthe paragraph that ends the sectionyes
remove_section_breaka section breaka sectionyes
set_page_setuporientation, paper, margins, first page, page numberingsectionsyes
add_header_footera new header or footera sectionits content; the new part and its reference are not
insert_tablea new tablea destinationyes
delete_tablea tablea tableyes
insert_table_row, delete_table_rowa rowa rowyes
insert_table_column, delete_table_columna columna columnyes
add_hyperlinka link on existing texta paragraph, and optionally a selectoryes
set_hyperlink, remove_hyperlinka link's address, or the linka paragraph, and optionally a selectoryes
refresh_fieldswhen Word updates fieldsnothingno
insert_table_of_contentsa new table of contentsa destinationyes; its update-on-open flags are not
delete_notea footnote or endnotea noteyes
add_commenta new commenta paragraph or range, and optionally a selectorno: a comment is an annotation
reply_comment, resolve_commenta comment threada commentno
accept_revision, reject_revisionexisting tracked changesrevisionssettles them
set_document_settingsTrack Changes, editing restriction, personal informationnothingno

Tracked and untracked

Every change to document content is a real Word tracked change carrying the request's author and date: the redline shows it for review, the clean copy accepts it, and accept_revision or reject_revision decides it like any other revision. What Word cannot track is applied directly, to both outputs, and stays when revisions are accepted or rejected:

  • set_document_settings: the settings it names (w:trackRevisions, w:documentProtection, w:removePersonalInformation) and the file properties' author names.
  • refresh_fields and insert_table_of_contents: fields' w:dirty flags and w:updateFields in the settings. The table of contents' paragraph is tracked.
  • Comments from add_comment, reply_comment and resolve_comment: annotations, not revisions.
  • New list instances (w:num) and definitions (w:abstractNum), and numbering.xml when the document has none, that set_numbering and insert_paragraphs lists need. The paragraphs that use them change as tracked changes.
  • A cross_reference run: the hidden _Ref bookmark on the paragraph pointed to, when it has none.
  • A footnote or endnote run: the notes part, with Word's separator notes, and the note styles, when the document lacks them.
  • add_hyperlink and hyperlink runs: the Hyperlink character style, when the document lacks one.
  • add_header_footer: the new header or footer part and the section's reference to it; its content is tracked.

Locators

Everything an edit names is a locator object, never a bare number. The document content prints every locator, so you copy locators instead of computing them.

Paragraph locators

{"index": 12}                                              // body paragraph 12
{"part": {"kind": "footnote", "number": 3}, "index": 0}    // first paragraph of footnote 3
{"part": {"kind": "header", "type": "first", "section": {"number": 2}}, "index": 0}
{"part": {"ref": "fn1"}, "index": 0}                       // a note or header this request creates
{"ref": "p1"}                                              // a paragraph this request creates
  • index. It counts the paragraphs of a part from 0, in the order the document content lists them, including paragraphs in table cells.
  • Tracked text. The document is seen as the content shows it: tracked insertions are included and tracked deletions left out. A paragraph whose mark is a tracked deletion is still listed and counted.
  • Parts.
    • kind is body (the default when part is omitted), footnote, endnote, header or footer.
    • A note's number is its position among notes of its kind, in document order. That isn't the mark the reader sees, which can be custom or restart per section. The document content prints both, as number and label.
    • A header or footer has a type (default, first or even) and a section locator. Together they name the part that section's pages show.
    • A part that several sections show through Word's linking is one part. The document content prints it once, with its canonical locator (the first section that shows it) and every section that shows it. A locator naming any of those sections reaches the same part.
    • Editing a shared part changes every section that shows it; the content's sections says which.
    • A section that insert_section_break creates shows the same parts as the section it was split from, as in Word, so its header locator reaches those parts.
    • Even-page headers and footers that already exist can be edited but not created. Creating one needs the document-wide even-and-odd setting, which would leave every other section's even pages blank.
  • Out of reach. Text boxes and comment bodies have no locator, and "all" doesn't reach them. When an edit over "all" leaves out a text box holding text it would otherwise have acted on, nothing reports it. Content controls sit inside paragraphs, so their text is ordinary paragraph text.

Other locators

NamesLocator
a section{"number": 2}; set_page_setup also takes an array of section locators, or "all"
a table{"number": 2}: the tables of a part in document order, an outer table before the tables nested in it. Body is the default; a table elsewhere adds part
a row or column{"table": {"number": 2}, "number": 4}, or a paragraph locator inside one of its cells ({"index": 142}), which names the row or column of the innermost table holding that paragraph. Columns count grid columns, and a paragraph in a cell that spans several grid columns can't name a column
a comment thread{"number": 3}: top-level threads in document order. Replies aren't counted, since replying and resolving act on a thread
a note{"kind": "footnote", "number": 3}
revisions{"number": 5}, one entry of the document content's revisions; "all"; or a filter {"author": "...", "kind": "insertion"} where both keys are optional and kind is insertion, deletion, move or property. property covers every formatting, paragraph, numbering, table and section property change

Any locator may instead be {"ref": "..."}, which names something this request creates (Refs).

Counting

index is the only value counted from 0, because it is a position in the content's arrays. Everything else counts from 1, as a reader counts: note, comment, revision, section, table, row and column numbers; occurrence; list level (1 to 9, as Word's interface shows it), so pattern writes level 3 as %3; and table-of-contents heading levels.

The paragraphs field

Every operation that acts on paragraphs names them in one required field, paragraphs:

FormNames
a locatorthat paragraph
{"from": locator, "through": locator}the uploaded paragraphs between the two, inclusive, in document order, within one part. A range of refs from one insert_paragraphs names those new paragraphs
an array of locators and rangeseach of them; a paragraph named twice counts once
"all"every paragraph of every part (body, headers, footers, footnotes, endnotes)
{"all": true, "part": {...}}every paragraph of the matching parts; a part with only kind matches every part of that kind. part is required in this form
  • No implicit scope. A whole-document replace says "paragraphs": "all".
  • Only uploaded paragraphs. The "all" forms, and ranges between uploaded locators, name uploaded paragraphs only (How a request is applied).
  • Ranges and tables.
    • A range that covers a whole table includes the table, so moving or deleting the range moves or deletes the table.
    • A range for delete_paragraphs, move_paragraphs or join_paragraphs that covers only part of a table is refused, whichever end is inside it. So is one that spans more than one cell. A range wholly inside one cell is fine.
    • Deleting every paragraph of a cell is refused.
  • How many. split_paragraph, add_hyperlink, set_hyperlink and remove_hyperlink take exactly one locator, since a Word hyperlink can't cross paragraphs. add_comment takes one locator or one range; a range makes one comment spanning it. join_paragraphs takes one range.

Destinations

insert_paragraphs, move_paragraphs, insert_table and insert_table_of_contents place content with "destination": {"paragraph": locator, "position": "before" | "after"}.

  • A destination at a paragraph in a table cell places the content in that cell. An insert_table there makes a nested table.
  • {"table": locator, "position": "before" | "after"} places content directly before or after a table.
  • Content placed after one paragraph or table comes before content placed before the next one.
  • insert_table_row uses {"row": locator, "position": ...}, and insert_table_column uses {"column": locator, "position": ...}.

Refs

An edit that creates something can carry "ref": "<name>", and later edits use {"ref": "<name>"} wherever that kind of locator is expected. Ref names form one namespace per request, and each is defined once, before it is used.

Created byNames
new_paragraphs itemsthe new paragraph
split_paragraphthe second half
insert_section_breakthe new section after the break; the original {"number": N} still names the section before it
insert_table, insert_table_row, insert_table_columnthe new table, row or column
add_header_footerthe new part: {"part": {"ref": "hdr1"}, "index": 0}
note runsthe new note: {"part": {"ref": "fn1"}, "index": 0}
add_commentthe new thread: reply_comment with "comment": {"ref": "c1"}

Selecting text

text selects text in the named paragraphs, and at names an insertion point:

"text": {"match": "Series A Preferred Stock", "prefix": "the ", "suffix": " issued", "occurrence": 2}
"at": "start" | "end"
"at": {"after": {"match": "Name:\t"}}       // a selector: prefix, suffix and occurrence apply
"at": {"before": {"match": "Exhibit"}}

insert and split_paragraph take at. An insert applies once at each point it names: with "start" or "end", once per named paragraph; with a selector, once per match.

  • What it matches. A selector matches only the uploaded text of the named paragraphs, and a match never crosses a paragraph boundary.
  • Matching rules.
    • Matching is case-sensitive.
    • In a match string, \t matches a tab run and \n a line break.
    • These characters are read alike: a non-breaking, thin or hair space and a space; curly and straight quotes; en and em dashes and a hyphen; a character with a one-character Unicode compatibility form and that form. Nothing else is folded (… is not ...).
  • Run form. A match is a string or an array of runs. The run form is for content with no characters: [{"type": "break", "kind": "page"}] matches a page break, for delete only, without prefix or suffix.
  • prefix and suffix. Optional text that must sit immediately before and after the match. They filter the candidates before counting, and the edit never acts on them.
  • occurrence. It counts across every paragraph the edit names, in document order: the body; for each section in turn, its header then its footer, each in the order default, first, even; footnotes; endnotes. A shared part counts once, at its canonical section.
    • Omitted: the match must be unique across them.
    • N: the Nth match, for example the second "Acme" in the document with "paragraphs": "all".
    • "all": every match. At least one must exist.
  • Character ranges are half-open. Two that touch end to end don't overlap.
  • One match when an edit creates something.
    • add_comment, add_hyperlink, set_hyperlink, remove_hyperlink and split_paragraph must select exactly one range or point.
    • So must an insert whose content holds a note run with a ref.
    • split_paragraph at "start" or "end" is refused, since it would make an empty paragraph. Use insert_paragraphs for that.
    • add_hyperlink over text that is already linked is refused. Use set_hyperlink.
  • Required or optional.
    • delete and replace require text.
    • format, add_comment and add_hyperlink without text act on the whole paragraph.
    • set_hyperlink and remove_hyperlink without text act on the paragraph's only hyperlink.
  • New content. Text in a paragraph this request creates can't be selected, since it isn't in the document content. The paragraph itself can be named by ref for paragraph-level edits. New sections, tables, rows, columns, notes and comments can be named by ref by any edit that takes that kind of locator.
  • Hidden content. Note references, comment anchors, bookmarks and images are skipped by matching, so "409A." matches across a footnote mark between "409A" and ".". A replace or delete whose range would contain a note reference or image is refused. Comment anchors and bookmarks stay in place.
  • Field results. A field's computed result counts as text, as the document content shows it, and can anchor a selector, but a replace, delete or format range holding part of a result, and an insertion point strictly inside one, are refused, since updating the field would undo the edit; edit the field's target instead. A range holding a whole result, such as "Section 1.3 below" where "1.3" is a REF result, removes the field and is accepted. A HYPERLINK field's result is ordinary link text.
  • Splits and joins. A split or join doesn't change what a selector sees. Text is selected through the uploaded paragraph that held it: the original locator of a split paragraph, or the merged-in paragraph's own locator.

A selector that matches nothing, or more than once without occurrence, is refused naming the count and, when the text is elsewhere, the paragraphs holding it:

edit at index 0 (replace): "Preferred" matches 3 times in paragraph 8; give prefix or suffix
so it matches once, or occurrence (1 to 3, or "all")

Signature lines

A signature line is usually a label, a plain tab to where the line starts, and an underlined tab drawing the line to the next tab stop ("Name:", tab, underlined tab). Insert the value after the label's tab, "at": {"after": {"match": "Name:\t"}}: it takes the plain tab's formatting and the underlined tab still draws the rest of the line. To have the value replace the line, replace the underlined tab: "text": {"match": "\t", "prefix": "Name:\t"}. The new text takes the tab's formatting, underlined; a run with "format": {"underline": false} gives plain text.

Content

New text is always content: in insert, replace, add_comment, reply_comment and add_header_footer, in note runs, in table cells and in new_paragraphs items. content is a string or an array of runs, in the shapes the document content uses.

RunInserts
{"text": "..."}text
{"type": "tab"}a tab
{"type": "break", "kind": "line" | "page" | "column"}a break; page and column breaks only in the body and its tables
{"type": "field", "code": "PAGE"}a field (Field and cross-reference runs)
{"type": "cross_reference", "to": locator, "show": "text" | "number" | "page" | "above_below"}a reference to a paragraph
{"type": "footnote" | "endnote", "content": ...} or {..., "new_paragraphs": [{"content": ...}]}a note, with one paragraph or several (Note runs)
{"type": "hyperlink", "url": "...", "content": ...}new linked text
  • Any run can carry format (Property values). A note run can carry a ref.
  • In a content string or a text run, \t becomes a tab run and \n a line break. New paragraphs come only from insert_paragraphs, split_paragraph and the new_paragraphs forms.
  • A table cell's value is content (one paragraph) or {"new_paragraphs": [...]}, whose items are insert_paragraphs items.
  • A comment's content (add_comment, reply_comment) is one paragraph: a string, or text, tab and line-break runs. Other runs are refused.
  • replace requires non-empty content. Removing text is a delete.
  • Plain new text takes the formatting of the text before it, or of the text after it at a paragraph's start, as typing does in Word; in an empty paragraph, the paragraph mark's. A replace's plain content takes the formatting of the first character it replaces.
  • Text an edit writes may not hold a control character other than tab, newline and carriage return, nor U+FFFE or U+FFFF, which a Word document cannot store.

Naming rules

  • Reserved keys. type, text and at are reserved among an edit's keys. Inside a run, type is the run's type and text its characters, as in the document content.
  • Property objects. As a key, format always holds a character-format object. Paragraph properties nest under paragraph_format and page properties under page_setup, the same objects the document content returns.
  • Field names. Styles are named by style_id. insert_paragraphs takes new_paragraphs, because paragraphs is a locator field.
  • Operation names. insert_* places new content at a position, and add_* attaches something to existing content. delete* removes content, and remove_* unwraps something and keeps its content.
  • Enums. Every enum value is snake_case: lower_roman, next_page, at_least.

Property values

For every property in format, paragraph_format and page_setup:

ValueEffect
a valuesets the property
falseturns it off, even where the style turns it on
nullremoves the direct setting, so the style decides
key omittedleaves the property alone
  • Toggles. bold, italic, small_caps, keep_with_next and the like take true, false or null.
  • Enums with an off state. underline, highlight and strike take an enum value, false for off, or null. true means "single" for underline and strike; it is refused for highlight.
  • Everything else. Other properties take a value or null.
  • Page setup. A section has no style for null to fall back to, so in page_setup only different_first_page and page_numbering (and its fields) take null.
  • Nested objects merge field by field.
    • For example, {"indent": {"left": 108}} keeps the paragraph's other direct indent values.
    • Setting first_line removes hanging, and setting hanging removes first_line.
    • null for a whole object ("indent": null) removes all of its direct values.
    • The same merge applies to margins, spacing (which holds before, after and line_spacing), page_numbering and settings.
    • line_spacing is one value in one of three forms ({"multiple": 1.15}, {"exact": 14} or {"at_least": 14}), so it is replaced whole.
  • Whole paragraphs. A format without text formats the paragraph mark too, as selecting a whole paragraph in Word does.
  • Removing everywhere. To "remove X everywhere", send false. null only removes direct formatting, so a heading that is bold through its style would stay bold.
  • Units. Indents, spacing, margins, paper size and font_size are in points.

How a request is applied

Three rules:

  1. Addresses mean the document as you read it. Every locator and selector refers to the uploaded document, the one the document content described, whatever earlier edits in the request have done.
  2. Edits apply in request order. When two edits set the same property of the same text or paragraph, the later one wins.
  3. An edit whose target an earlier edit removed or rewrote is refused, and so is the whole request.

The result is the same as sending each edit as its own request in turn, except that every edit's addresses come from the one read of the document. The request is all or nothing: if any edit is refused, or fails while being applied, nothing is applied and no file is produced. Decisions on existing tracked changes (accept_revision, reject_revision) go alone: a request that contains one contains only decisions. To accept changes and then edit, send two requests, the second against the first's result. See Edit for the request and response around the edits array.

Addresses

  • Indexes don't shift. If edit 1 inserts a paragraph after paragraph 5, edit 2's {"index": 10} still means the paragraph the document content called 10, now eleventh in the document.
  • Positions don't shift. A selector matches the uploaded text of the paragraphs it names. Text that earlier edits inserted is never matched; text they removed can't be matched (rule 3).
  • Wide forms. "all", {"all": ...}, ranges and arrays name uploaded paragraphs. Paragraphs an earlier edit removed are skipped.
  • Refs name what an earlier edit created. A ref used before the edit that creates it is refused. A new paragraph can be named by ref for paragraph-level edits: set_paragraph_style, set_paragraph_format, set_numbering, format without text, add_comment without text, insert at "start" or "end", and as a destination or cross-reference to. Its text can't be selected, since it isn't in the document content; give it its formatting in the creating edit, or format the whole paragraph.
  • Moved paragraphs. A locator for a paragraph that an earlier edit moved still names it, in its new place. A destination at it places content next to its new place.
  • Split paragraphs. The original locator names both halves for paragraph-level edits. Its text is selected through the original locator. The second half can also be named by the split's ref.
  • Joined paragraphs. A paragraph that an earlier join_paragraphs merged away can't be named for paragraph-level edits. Its text is still selected through its own locator, since joining keeps the text.
  • Deleted paragraphs as destinations. A destination at a paragraph an earlier edit deleted places the content where that paragraph was.
  • style_template styles are imported before the first edit.
  • Field results are computed after the last edit, so a new table of contents or cross-reference reflects the edited document. refresh_fields does the same for existing fields. Results that need page layout are those the field tables below say the API can compute; any others are marked for Word to update on open.

Order

  • Later wins. "Unbold everything" then "bold Purchase Price" leaves only Purchase Price bold. Listed the other way round, everything ends up unbolded.
  • Lists and indents. The indent a list level brings never overrides a direct indent, in either order, as in Word.
  • Same position. Content inserted at the same point appears in request order, across operation types.
    • A replace puts its new text at the start of its range, so an insert at that point and the replacement come out in request order.
    • An insert at the end of a replaced or deleted range goes after it.
    • Two destinations are the same point only when they name the same paragraph or table and position. Content placed after one paragraph or table comes before content placed before the next one.
    • Content placed after the paragraph holding a new section break goes into the new section.
  • Comments. Comments added to the same range, and replies to one thread, appear in request order. A comment's range follows its text, so a later replace inside it leaves the deleted and the new text inside the comment.
  • Revision decisions apply in request order too, and the later decision wins for a revision. "Reject everything, then accept author A's" is reject_revision with "all", then accept_revision with {"author": "A"}.

Positions

  • Points. An insertion point is a position in the uploaded text. "start" is before everything in the paragraph, and "end" after everything, hidden content included.
  • Hidden content. Matching skips note references, comment anchors, bookmarks and tracked deletions, so a point next to them needs a side.
    • A point after a match sits immediately after its last character, before any hidden content.
    • A point before a match sits immediately before its first character, after any hidden content.
    • In a paragraph ending in a footnote mark, after the last word and "end" are different points.
  • Formatting of plain new text. See Content.
  • Clearing never deletes characters. A leading tab is removed with a delete.
  • New table cells copy the paragraph and run formatting of a neighbouring cell. A new row copies the row above (or below, for a new first row), and a new column copies the column to the left (or right, for a new first column).

Refused

Every refusal in a request is reported together, naming the edits involved.

Rule 3: the target is gone or rewritten.

An edit that...after an earlier edit...
selects text (text or an at anchor; prefix and suffix excluded) overlapping charactersreplaced or deleted them
has an insertion point strictly inside a rangereplaced or deleted that range
names a paragraph for a paragraph-level edit, or as a destinationmerged it away with join_paragraphs, or deleted it (except as a destination, above)
names a table, row, column, section or notedeleted it, or removed the section's break
has an insertion point at a split point, or a replace or delete crossing onesplit the paragraph there
splits inside a rangereplaced or deleted that range
names a header or footer through a section that now shows a different partadded a header or footer with add_header_footer (name the new part by its ref)

prefix and suffix only check context, so they may overlap text an earlier edit changed.

Refused in any order.

CombinationWhy
A replace, delete, insert or format inside a field resultUpdating the field would undo it. Edit the field's target instead
Two add_hyperlink ranges that overlapWord can't nest hyperlinks
A move_paragraphs whose destination is inside the paragraphs it movesThe destination moves with them
A row insert inside a vertically merged span, or a column insert or delete through a cell spanning several grid columnsThe grid can't keep or split the merge
Selecting text in a paragraph this request createsIt isn't in the document content. Describe it in the creating edit
A ref on something an edit would create more than onceIt couldn't name one thing

Text

insert

insert puts content at at in each named paragraph: once per paragraph with "start" or "end", once per match with a selector. Inserted content is a tracked insertion (w:ins); accepting keeps it and rejecting removes it.

{"type": "insert", "paragraphs": {"index": 304}, "at": {"after": {"match": "Name:\t"}}, "content": "Jordan Bryan"}
{"type": "insert", "paragraphs": {"index": 27}, "at": "start", "content": "2026"}
{"type": "insert", "paragraphs": {"index": 31}, "at": {"after": {"match": "Signature:"}},
 "content": [{"type": "tab", "format": {"underline": true}}, {"type": "break", "kind": "line"},
             {"text": "Name and title", "format": {"bold": true}}]}
{"type": "insert", "paragraphs": {"index": 9}, "at": {"after": {"match": "See "}},
 "content": [{"type": "hyperlink", "url": "https://example.com/schedule-a", "content": "Schedule A"}]}

To fill an empty paragraph, insert at "start". A break is a run holding w:br (a line break has no type; w:type="page" or "column") and a tab one holding w:tab, inside the insertion. A hyperlink run is written as a HYPERLINK field, the form in which Word can track a link, in one w:ins.

Page breaks

A page break is a break run of kind page. Insert one with insert, and remove one with delete whose match is that run (with occurrence when the paragraph holds several). Each is tracked: the break's run in w:ins, or in w:del split from any text sharing it. A paragraph that starts a new page through page_break_before holds no break: set_paragraph_format with "page_break_before": false removes the property. Turning a page break into a section break that restarts page numbering is three edits:

[{"type": "delete", "paragraphs": {"index": 129}, "text": {"match": [{"type": "break", "kind": "page"}]}},
 {"type": "insert_section_break", "paragraphs": {"index": 129}, "section_type": "next_page", "ref": "schedule"},
 {"type": "set_page_setup", "section": {"ref": "schedule"}, "page_setup": {"page_numbering": {"start": 1}}}]

Inserting one:

{"type": "insert", "paragraphs": {"index": 42}, "at": "start", "content": [{"type": "break", "kind": "page"}]}

A page or column break goes in the body and its tables only: Word offers neither in a header, footer, note or text box.

replace and delete

replace deletes the selected text and inserts content (required, non-empty) at the start of its range; delete deletes it. Deleted text is a tracked deletion (w:del), new text a tracked insertion.

{"type": "replace", "paragraphs": {"index": 8}, "text": {"match": "Preferred", "prefix": "other Series A "}, "content": "Common"}
{"type": "replace", "paragraphs": "all", "text": {"match": "Acme Ltd", "occurrence": "all"}, "content": "Acme Holdings Ltd"}
{"type": "delete", "paragraphs": {"index": 71}, "text": {"match": "[", "occurrence": "all"}}

A replace or delete whose text isn't in the named paragraphs is refused, naming the paragraphs where the text is. A page break is removed with delete; replace can't select one.

format

format changes the character formatting of the selected text, or of whole paragraphs, marks included, without text. The text is split into runs of its own, each recording its previous formatting in a w:rPrChange, a tracked formatting change: the redline shows "Formatted" changes and the clean copy keeps the new formatting.

{"type": "format", "paragraphs": {"index": 12}, "text": {"match": "Effective Date"}, "format": {"bold": true, "highlight": "yellow"}}
{"type": "format", "paragraphs": "all", "format": {"bold": false}}
format propertyValues
bold, italic, small_caps, all_caps, superscript, subscripttrue, false or null
underlinetrue (single), single, double, thick, dotted, dash, wave, words, false or null
striketrue (single), single, double, false or null
highlighta Word highlight color (yellow, green, cyan, magenta, blue, red, darkBlue, ..., lightGray), false or null
colorsix hex digits (1F3864), or null
font_sizepoints, in halves (10.5), or null
fonta font family name, or null

At least one property is required, and superscript and subscript cannot both be true. The document content reads formatting back in the same terms: each text segment's format holds the properties above that the text has through its styles; color, font_size and font appear where they differ from the document's default text formatting.

Paragraphs

insert_paragraphs

insert_paragraphs places new_paragraphs at a destination, each item a Word paragraph in array order and a tracked insertion of its text and paragraph mark.

{"type": "insert_paragraphs", "destination": {"paragraph": {"index": 24}, "position": "after"},
 "style_id": "Normal", "list": {"new": "bulleted"}, "paragraph_format": {"indent": {"left": 108, "hanging": 18}},
 "new_paragraphs": [{"content": "Constitutional Amendments.", "style_id": "Heading3", "list": null},
                    {"content": "First Amendment (1791): freedom of religion and speech"},
                    {"content": [{"text": "Second Amendment", "format": {"italic": true}}, {"text": " (1791)"}], "ref": "second"}]}
  • Items. Each takes content (omitted, an empty paragraph) and, optionally, style_id, paragraph_format, list, level (1 to 9) and ref.
  • Operation-level values. style_id, paragraph_format, list and level on the edit apply to items that don't set their own; an item's paragraph_format merges over the operation's. With no style from either, the document's default paragraph style applies.
  • list. {"new": "bulleted" | "numbered"} on the edit makes its items one new list; on an item it starts a list of its own. {"same_as": locator} joins an existing list. An item's "list": null leaves it out of the operation's list, so its style's numbering, if any, applies; false gives it no numbering. level defaults to the operation's, else the same_as paragraph's, else 1.

A destination is a paragraph directly in the body or a table cell. A missing paragraph style fails the edit with INVALID_EDIT. For Markdown input, parse it in the client and send each paragraph's text, runs and style; .md and .txt uploads are unsupported, so start from a blank DOCX.

delete_paragraphs

delete_paragraphs deletes whole paragraphs as tracked deletions of their text and paragraph marks, so accepting it leaves the following paragraph exactly as it was. A range covering a whole table deletes the table's rows as tracked row deletions.

{"type": "delete_paragraphs", "paragraphs": [{"from": {"index": 9}, "through": {"index": 11}}, {"index": 171}]}

Refused: a range covering part of a table or more than one of its cells; every paragraph of a cell (delete the row, column or table); the last paragraph of the document, a header, a footer or a note (delete its text with delete); a paragraph that ends a section; and paragraphs that are all that keeps two tables apart, since Word joins tables that touch.

move_paragraphs

move_paragraphs moves paragraphs, marks included, to a destination as a Word tracked move. A range covering whole tables moves them too: Word can't track a table move, so the table's rows are deleted where they were and an inserted copy, keeping the ids, goes to the destination.

{"type": "move_paragraphs", "paragraphs": {"from": {"index": 9}, "through": {"index": 11}},
 "destination": {"paragraph": {"index": 19}, "position": "after"}}

At the old place a copy of each paragraph stays as moved-from content (w:moveFrom, between w:moveFromRangeStart and w:moveFromRangeEnd); at the destination the paragraphs themselves, keeping their ids, are moved-to content (w:moveTo), the two ranges sharing one w:name. Accepting leaves the paragraphs at the destination only; rejecting restores them, and the copies have new ids. Deciding either half decides the whole move. A moved footnote or endnote reference refers to a new note holding the note's paragraphs, marked inserted, while the old note's text is marked deleted; deciding the move decides them, so each note is in the document once either way. Bookmark and comment anchors inside the moved paragraphs move with them; a bookmark reaching outside them that no field or hyperlink names keeps its inside end at the old place.

Refused: a destination among the moved paragraphs, where they already are, or after the last paragraph of the body or a cell; paragraphs outside the body and its tables; a range covering part of a table; moving the last paragraph of the body or a cell, a paragraph that ends a section, or paragraphs that keep two tables apart; moved paragraphs or their notes holding tracked changes, pictures, embedded objects, content controls, permission ranges, or a field that fetches or runs content (INCLUDETEXT, INCLUDE, INCLUDEPICTURE, DDE, DDEAUTO, IMPORT, LINK, MACROBUTTON, DATABASE, RD); part of a field or comment reaching outside them; and a moved table holding tracked changes, notes, comments or bookmarks.

split_paragraph

split_paragraph ends a paragraph at one point with a new paragraph mark, as pressing Enter does. at is a selector matching once; "start" and "end" are refused (use insert_paragraphs). ref names the second half.

{"type": "split_paragraph", "paragraphs": {"index": 12}, "at": {"after": {"match": "as follows:"}}, "ref": "rest"}

The split is tracked as an inserted paragraph mark (w:pPr/w:rPr/w:ins) ending the first half, which keeps the paragraph's id; the second half has a new id. Both keep the paragraph's properties, and the second half takes the original mark with what it carries (a section break, the mark's formatting). Accepting keeps two paragraphs; rejecting merges them back. The original locator names both halves for paragraph-level edits. A point inside a hyperlink, a field, a content control or a tracked change fails the edit with INVALID_EDIT.

join_paragraphs

join_paragraphs merges a range of paragraphs into its first, as deleting the pilcrows does in Word.

{"type": "join_paragraphs", "paragraphs": {"from": {"index": 40}, "through": {"index": 41}}}

Each join is tracked as a deleted paragraph mark (w:pPr/w:rPr/w:del). The joined paragraph keeps the first paragraph's properties: where a following paragraph's differ, they change as a tracked property change (w:pPrChange). Accepting leaves one paragraph; rejecting restores them. Refused: a range of fewer than two paragraphs, across table cells or partly in a table; a paragraph ending a section, one whose mark is already deleted, or one followed by a table.

set_paragraph_style

set_paragraph_style gives paragraphs a paragraph style of the document or of the style_template. clear takes any of typography, numbering and paragraph, or "all".

{"type": "set_paragraph_style", "paragraphs": [{"index": 3}, {"from": {"index": 5}, "through": {"index": 9}}],
 "style_id": "Heading3", "clear": "all"}
  • typography: direct font family (theme fonts included), size, colour and character spacing. Bold, italics, underline, caps, highlight and character styles are kept.
  • numbering: direct list numbering.
  • paragraph: spacing, indentation, alignment, tab stops and outline level. Pagination and section boundaries are kept.

Anything outside those three categories, such as borders and shading, is kept.

The style and the cleared paragraph properties are one tracked paragraph property change (w:pPrChange), and cleared typography a tracked formatting change (w:rPrChange). Clearing never deletes text: a leading tab is removed with a delete. A style the paragraph already has, with nothing to clear, records nothing. A style the document and style_template lack fails the edit with INVALID_EDIT.

set_paragraph_format

set_paragraph_format sets alignment, indents, spacing and pagination.

{"type": "set_paragraph_format", "paragraphs": [{"index": 12}, {"index": 13}],
 "paragraph_format": {"alignment": "justify", "indent": {"left": 36, "first_line": 18},
                      "spacing": {"before": 0, "after": 6, "line_spacing": {"multiple": 1.15}}, "keep_with_next": true}}
PropertyValues
alignmentleft, center, right, justify, or null
indentleft and right (points, -1584 to 1584), and first_line or hanging (0 to 1584)
spacingbefore and after (points, 0 to 1584), line_spacing
keep_with_next, keep_lines_together, page_break_before, widow_controltrue, false or null

Measures are rounded to the twentieth of a point and replace the paragraph's own value in every form Word reads. The change is tracked: each paragraph's previous properties go into a w:pPrChange, so rejecting restores them; a format the paragraph already has records nothing. Header, footer, note and table-cell paragraphs are formatted the same way. The document content gives each paragraph a paragraph_format in these terms where it sets a property or differs from the document's default.

set_numbering

set_numbering changes list numbering by action. Levels count from 1, as the document content's level does.

{"type": "set_numbering", "paragraphs": {"from": {"index": 20}, "through": {"index": 22}}, "action": "apply", "list": {"same_as": {"index": 19}}}
{"type": "set_numbering", "paragraphs": [{"index": 5}, {"index": 6}], "action": "apply", "list": {"new": "bulleted"}, "level": 1}
{"type": "set_numbering", "paragraphs": {"index": 21}, "action": "set_level", "level": 2}
{"type": "set_numbering", "paragraphs": {"index": 30}, "action": "restart", "start": 1}
{"type": "set_numbering", "paragraphs": {"index": 31}, "action": "continue"}
{"type": "set_numbering", "paragraphs": {"index": 40}, "action": "remove"}
{"type": "set_numbering", "paragraphs": {"from": {"index": 24}, "through": {"index": 41}},
 "action": "set_number_format", "level": 3, "number_format": "lower_roman", "pattern": "(%3)"}
  • apply. The paragraphs become one list instance: a {"new": "bulleted" | "numbered"} list, with Word's own nine levels for its Bullets or Numbering button, or the list of a same_as paragraph. Each keeps its level (1 if it had none) unless level is given.
  • restart and continue. At the first named paragraph, restart numbers from start, and continue resumes the previous list of the same definition, as Word's "Continue numbering" does. The items after it in its list, up to the next item at a higher level, go with it.
  • set_number_format. Changes one level's number_format (decimal, decimal_zero, lower_roman, upper_roman, lower_letter, upper_letter, ordinal, cardinal_text, ordinal_text, bullet, none) and optional pattern ((%3) shows level 3's number in brackets) for the named paragraphs at that level. A run is a maximal sequence of named paragraphs at that level with only deeper-level paragraphs between them; a paragraph at that level that isn't named, or any paragraph at a parent level, ends the run. Each run gets its own list instance, with that level overridden, starting at the run's first current number, so numbers keep their values and change only their format; the deeper-level paragraphs between and directly after them move into it, so numbering still restarts under each item. A pattern showing another level's number is refused, and so, when the edit runs, is a deeper level moving into the instance whose pattern shows a level above the changed one (level 4 "%2.%4" when level 3 changes).

Each paragraph's numbering change is a tracked paragraph property change (w:pPrChange); removing numbering a style gives writes w:numId 0, as Word does. The list instances and definitions a new list, a restart or a format change needs are added untracked: Word cannot track numbering definitions. The document content shows each numbered paragraph's number, level and list_id, and list_styles. Refused: a paragraph that isn't numbered for set_level, restart, continue or remove, a level the list doesn't define, and a continue with no earlier list of the same definition.

Sections

A section break is the end of a paragraph that carries the properties of the section it ends: the document content shows them as that paragraph's section, and the last section's as final_section. OOXML keeps one record of a section's earlier properties, so a section edit that must record a change on a section whose properties already carry a tracked change is refused (accept or reject it first).

insert_section_break

insert_section_break ends a section at each named paragraph, which must be directly in the body, not already end a section and not be the body's last paragraph. section_type is how the new section starts: next_page, continuous, even_page or odd_page. ref names the section after the break; the original number still names the one before it.

{"type": "insert_section_break", "paragraphs": {"index": 129}, "section_type": "next_page", "ref": "schedule"}

Both sections keep the original's page setup, columns, headers and footers; the new one starts as section_type says. The break is tracked as Word tracks an inserted section break: the paragraph ends with an inserted paragraph mark carrying the new section's properties, and its original mark, tracked as deleted, ends an empty paragraph after it; a change to how the following section starts is a w:sectPrChange. All of it is one revision.

remove_section_break

remove_section_break removes the break that ends section. Its content joins the next section and takes that section's properties, headers and footers, as in Word.

{"type": "remove_section_break", "section": {"number": 2}}

The merged section starts as the removed one started (its section type and page-number restart), a tracked w:sectPrChange. The break is tracked as a deleted paragraph mark carrying the removed section's properties; a paragraph holding nothing but the break is deleted whole. All of it is one revision. Refused: the last section, a break that is itself a tracked insertion (reject that revision instead), and a paragraph mark already tracked as deleted or moved.

set_page_setup

set_page_setup changes the page of section: a section locator, an array of them, or "all".

{"type": "set_page_setup", "section": "all",
 "page_setup": {"orientation": "landscape", "paper": "a4", "margins": {"left": 72, "right": 72, "top": 54},
                "different_first_page": true, "page_numbering": {"start": 1, "number_format": "lower_roman"}}}
FieldValues
orientationportrait or landscape
paperletter, legal or a4; or page_width and page_height (points, 72 to 1584) instead
marginspoints by side: top, bottom, left, right, header, footer, gutter
different_first_pagetrue gives the first page its own header and footer (w:titlePg)
page_numberingstart (0 to 32767) and number_format (decimal, lower_roman, upper_roman, lower_letter, upper_letter)

null is accepted only for different_first_page and page_numbering (and its fields), since a section has no style to fall back to. The change is tracked: the section's earlier properties are kept in a w:sectPrChange, and a setting the section already has records nothing. different_first_page alone leaves the first page with whatever first-page header and footer the section has, usually none; add them with add_header_footer.

add_header_footer

add_header_footer gives a section a header or footer of its own: part names kind (header or footer), type (default or first) and section. Its text is content (one paragraph) or new_paragraphs (1 to 100, each content, style_id and paragraph_format). ref names the new part.

{"type": "add_header_footer", "part": {"kind": "footer", "type": "default", "section": {"number": 1}}, "ref": "ftr",
 "new_paragraphs": [{"content": [{"text": "Page "}, {"type": "field", "code": "PAGE"}, {"text": " of "},
                                 {"type": "field", "code": "NUMPAGES"}], "paragraph_format": {"alignment": "center"}}]}
{"type": "format", "paragraphs": {"part": {"ref": "ftr"}, "index": 0}, "format": {"font_size": 9}}

The content is tracked as Word tracks typing into a new header: every run is a tracked insertion and every paragraph mark but the last an inserted one, so rejecting leaves it empty. The new part (header1.xml, footer1.xml, ...) and the section's reference to it are applied untracked: Word records neither as a revision. The section stops linking to an earlier section's part, and later sections that linked to it show the new one. A first part shows only while different_first_page is on, and adding one doesn't turn that on. Refused: a section that already has its own part of that kind and type (edit its paragraphs), and an even part, since creating one needs the document-wide even-and-odd setting. After it, a locator naming that header or footer through a section that now shows the new part is refused; name the new part by its ref.

Tables

The document content shows a table's number, id and style_id, each row's header flag and tracked change, and each cell's row, column, span and change. Every table edit is tracked, and accept_revision and reject_revision decide each row, cell, grid and text revision it makes. A cell value is content (one paragraph; \n is a line break) or {"new_paragraphs": [...]}, whose items are insert_paragraphs items (content, style_id, paragraph_format, list, level, ref).

insert_table

insert_table places a table at a destination. rows holds each row's cell values, every row the same length (at most 63); header_row (default false), when true, repeats the first row on each page and gives it the table style's header-row formatting. ref names the table.

{"type": "insert_table", "destination": {"paragraph": {"index": 49}, "position": "after"}, "header_row": true,
 "rows": [["Class", "Shares authorized"], ["Common stock", "[__________]"],
          [{"new_paragraphs": [{"content": "Preferred stock"}, {"content": "Series A", "style_id": "Normal"}]}, "—"]]}

Every row is a tracked row insertion (w:trPr/w:ins) and its text a tracked insertion, so rejecting removes the table. The table has single borders like Word's Table Grid, and its columns share the text width of the section (or of the cell, for a nested table). Where it would touch another table or end its container, an empty paragraph with a tracked inserted mark is added beside it. Headers, footers and notes hold tables too, tracked the same way.

delete_table

delete_table deletes a table as tracked row deletions of every row and its text; accepting removes it.

{"type": "delete_table", "table": {"number": 3}}

A row another author already deleted is left as it is.

insert_table_row and delete_table_row

insert_table_row adds a row before or after row, with one cell value per grid column; ref names it. delete_table_row deletes a row as a tracked row deletion (w:trPr/w:del) with its text deleted.

{"type": "insert_table_row", "destination": {"row": {"index": 311}, "position": "after"}, "ref": "google",
 "cells": ["Google LLC", "Google Cloud Support", "AI-Enhanced PDF Conversion"]}
{"type": "delete_table_row", "row": {"table": {"number": 1}, "number": 4}}

The new row copies the neighbouring row's cell layout and formatting, without its revisions, vertical merges or header flag (the row above, or below for a new first row), and is a tracked row insertion with its text tracked. Each new cell's text takes the run formatting carried by the most plain text in the neighbouring cell, as text typed into a row Word inserts does. Refused: a row inside a vertically merged span, and a cell count other than the grid's.

insert_table_column and delete_table_column

insert_table_column adds a grid column before or after column, with one cell value per row; delete_table_column deletes a column.

{"type": "insert_table_column", "destination": {"column": {"table": {"number": 2}, "number": 3}, "position": "after"},
 "cells": ["Q3", "12", "9"]}
{"type": "delete_table_column", "column": {"index": 143}}

Word does not record inserting or deleting a column as one tracked change; it displays and decides tracked cell insertions and deletions, which is what these edits write. Each new cell is a tracked cell insertion (w:cellIns) with its text a tracked insertion, formatted like the cell beside it, and the table grid's earlier columns are kept in w:tblGridChange, which carries no author: a decision that settles all of a table's cell insertions and deletions settles its grid change the same way. A deleted column's cells are tracked cell deletions (w:cellDel) with their text deleted; accepting gives each cell's width to the cell before it. Refused: a column through a cell spanning several grid columns, a cell count other than the table's rows, and a paragraph locator in a spanning cell, which names no one column.

Field and cross-reference runs

A field run inserts a document field by its Word field code; a cross_reference run inserts a field pointing to another paragraph, to.

{"type": "insert", "paragraphs": {"part": {"kind": "footer", "type": "default", "section": {"number": 1}}, "index": 0},
 "at": {"after": {"match": "Page "}}, "content": [{"type": "field", "code": "PAGE \\* ROMAN"}]}
{"type": "insert", "paragraphs": {"index": 12}, "at": {"after": {"match": "see Section "}},
 "content": [{"type": "cross_reference", "to": {"index": 30}, "show": "number"}]}
Field codeResult the API writes
PAGE, NUMPAGES, SECTIONPAGES1, which Word replaces when it lays out the pages
DATEthe edit's date; Word shows the current date on open
CREATEDATE, SAVEDATEthe document's created or last-saved date
TITLE, AUTHORthe document's title or author
DOCPROPERTYthe named property: a custom property, or Title, Subject, Author, Keywords, Comments, Category, Company, Manager, LastSavedBy
FILENAMEnone; marked for Word to compute on open

A field is one tracked insertion: begin, instruction, result and end inside a w:ins, in the formatting of the text before it. No field that fetches or runs content (INCLUDETEXT, INCLUDEPICTURE, DDE, DDEAUTO, IMPORT, LINK, MACROBUTTON, ...) is offered.

showFieldResult
numberREF _Ref... \r \hthe paragraph's list number, without trailing periods (2.1)
textREF _Ref... \hthe paragraph's text
pagePAGEREF _Ref... \h1, marked for Word to compute on open
above_belowREF _Ref... \p \h"above" or "below", by where the paragraph is

A cross-reference's result is the one its paragraph has after the last edit, with every revision accepted. The paragraph pointed to needs a hidden _Ref bookmark around its text: an existing one is reused, otherwise one is added, untracked, and stays if the cross-reference is rejected; the field is a tracked insertion. number for a paragraph without a list number, and text for one without text, are refused.

add_hyperlink links existing text: the one match of text, or the whole paragraph without it. url is an absolute http, https or mailto address.

{"type": "add_hyperlink", "paragraphs": {"index": 4}, "text": {"match": "our website"}, "url": "https://example.com"}

The link is a HYPERLINK field, the form Word can track: its code is inserted before and after the text (w:ins) and the text takes the Hyperlink character style as a tracked formatting change. Accepting gives the link; rejecting restores the text. The Hyperlink style is added, untracked, when the document has none. Refused: addresses that run code, reach local files or embed content (javascript:, vbscript:, file:, data:), other schemes, relative and UNC addresses, and addresses with spaces, quotes, backslashes or angle brackets (percent-encode them); text that is already a link (use set_hyperlink); and two add_hyperlink ranges that overlap.

set_hyperlink gives a hyperlink a new url; remove_hyperlink unlinks it and keeps its text. With text, the hyperlink holding the match; without it, the paragraph's only hyperlink.

{"type": "set_hyperlink", "paragraphs": {"index": 4}, "text": {"match": "our website"}, "url": "https://example.com/new"}
{"type": "remove_hyperlink", "paragraphs": {"index": 4}}

Both are tracked: a new address deletes the old field code (w:delInstrText in w:del) and inserts the new one; unlinking deletes the field code and removes the Hyperlink style as a tracked formatting change. A link held as a w:hyperlink element or a simple field is first rewritten as the equivalent HYPERLINK field. Refused when the selection covers no hyperlink, or more than one. The document content shows a link on its segments as "link": {"url": ..., "anchor": ...}, and each paragraph's fields and bookmarks.

refresh_fields

refresh_fields marks every field in the body, headers, footers and notes for update (w:dirty) and sets w:updateFields, so Word asks the reader to update fields when the document opens.

{"type": "refresh_fields"}

Both changes are applied untracked, to both outputs. A document holding a field that fetches or runs content (as listed for moves, RD included) is refused, since refreshing would run it on open.

insert_table_of_contents

insert_table_of_contents places a TOC field as a new paragraph at a destination in the body outside tables. from_level and to_level (1 to 9) are the heading levels; hyperlinks (\h) and page_numbers default to true.

{"type": "insert_table_of_contents", "destination": {"paragraph": {"index": 2}, "position": "after"},
 "from_level": 1, "to_level": 3}

The defaults with levels 1 to 3 give Word's own TOC \o "1-3" \h \z \u; "page_numbers": false adds \n. The API cannot lay out pages, so the field shows a placeholder and is marked for update, with w:updateFields set, both untracked: Word writes the entries when the document opens. The paragraph is one tracked insertion. A heading above it is an insert_paragraphs at the same destination, earlier in the request.

Footnotes and endnotes

Note runs

A footnote or endnote run in an insert's content adds a note whose reference goes at that point. Its text is content, or new_paragraphs for several paragraphs; it holds text, tabs, line breaks, fields, cross-references and links, not page or column breaks or other notes. ref names the note, and {"part": {"ref": ...}, "index": n} its paragraphs.

{"type": "insert", "paragraphs": {"index": 56}, "at": {"after": {"match": "409A."}},
 "content": [{"type": "footnote", "ref": "fn1",
              "content": [{"text": "As defined in "}, {"text": "Section 1", "format": {"italic": true}}, {"text": "."}]}]}
{"type": "insert", "paragraphs": {"index": 30}, "at": {"before": {"match": "Schedule"}},
 "content": [{"type": "endnote", "content": "See the Schedule."}]}

The reference and the note's text are tracked insertions, written as Word writes a note inserted with Track Changes on, and share one revision, so deciding it decides the note whole. The notes part, with Word's separator notes, and the note styles are added untracked when the document lacks them.

delete_note

delete_note deletes a note and its reference mark.

{"type": "delete_note", "note": {"kind": "footnote", "number": 2}}

The reference run and the note's text become tracked deletions; accepting removes the note. Text edits reach a note's paragraphs through its part locator. The document content shows each note's part, label, paragraph (where its reference is), text_before (the 200 characters before the reference) and blocks, its paragraphs.

Comments

add_comment

add_comment anchors a comment to the one match of text in a paragraph, to the whole paragraph, or to a range of paragraphs. content is a string or text, tab and line-break runs; author overrides the request's for this comment; ref names the thread.

{"type": "add_comment", "paragraphs": {"index": 7}, "text": {"match": "30 days"}, "content": "Should this be 45?", "ref": "c1"}
{"type": "add_comment", "paragraphs": {"from": {"index": 25}, "through": {"index": 27}}, "content": "Double check this section."}

A comment is not a tracked change: it appears in the redline's comment pane and is kept in the clean copy, unless its reference goes with deleted text. A comment's range follows its text, so a later replace inside it leaves the deleted and the new text inside the comment. Comments go on body and table-cell paragraphs; Word doesn't support them in headers or footers, and they aren't added to notes.

reply_comment and resolve_comment

reply_comment answers a thread, and resolve_comment marks it resolved. comment is a thread's number from the document content's comments, or the ref of a thread this request adds.

[{"type": "reply_comment", "comment": {"ref": "c1"}, "content": "Agreed; changed to 45.", "author": "Lawyer A"},
 {"type": "resolve_comment", "comment": {"number": 3}}]

Replies and resolutions are annotations, applied untracked. Replies to one thread appear in request order. The document content gives each thread's number, author, date, text, paragraph, anchor_text, resolved and replies.

Tracked revisions

accept_revision and reject_revision

accept_revision and reject_revision decide the document's existing tracked changes. revision is one entry of the document content's revisions ({"number": n}), "all", or a filter by author and/or kind (insertion, deletion, move, or property: every formatting, paragraph, numbering, table and section property change).

[{"type": "reject_revision", "revision": "all"},
 {"type": "accept_revision", "revision": {"author": "Jane Doe"}},
 {"type": "accept_revision", "revision": {"number": 4}}]

A request holding a decision holds only decisions; to decide and then edit, send two requests. Decisions apply in request order and the later one wins for a revision, so the example rejects everything but Jane Doe's changes and revision 4. Accepting keeps what an insertion or a move's destination adds, drops what a deletion or a move's source removes, and keeps a property change's new properties; rejecting does the opposite. A decision is not itself a tracked change: the redline keeps every revision no decision covered, under its original author and date. Decisions cover the body, headers, footers and notes. Rejecting all of an edit set's revisions gives back the document it was applied to. An empty filter is refused. One revision entry is one thing to decide: a move (both halves), one author's contiguous insertion or deletion, or one property change; a replacement is two entries. A Word 2003 numbering change (w:numberingChange) cannot be rejected; rejecting it fails the edit.

Document settings

set_document_settings

set_document_settings sets how the output behaves in Word. Settings are not document content and Word cannot track them, so they are applied untracked to both outputs, and deciding revisions doesn't change them.

{"type": "set_document_settings",
 "settings": {"track_changes": true, "protection": {"edit": "tracked_changes", "password": "s3cret"},
              "remove_personal_information": true}}
FieldEffect in the output
track_changestrue turns on Track Changes (w:trackRevisions); false turns it off
protectionRestrict Editing, enforced (w:documentProtection), replacing any protection the document had. edit is tracked_changes (every change is tracked and tracking can't be turned off), read_only or comments. With password, Word asks for it to stop the protection. false removes protection
remove_personal_informationtrue sets Word's "remove personal information from file properties on save" and clears the output's creator and last-modified-by names; false turns it off

Fields left out are left as the document has them; at least one is required. password is 1 to 15 printable ASCII characters, hashed when the request arrives as Word hashes it, and only the hash is stored and written; Restrict Editing guards against accidental changes and is not encryption. A request holds at most one set_document_settings edit. track_changes: false with tracked_changes protection is refused. The even-and-odd headers setting isn't included. The document content shows the document's own settings.

  • Edit: upload, read, send edits, fetch the result
  • Compare: two documents, one redline
  • Errors: every error code and what to do about it