edits is a JSON array of edit objects, applied in request order. Each has a type and the fields that
type defines; unknown fields are refused. Their machine-readable definitions are in the OpenAPI specification
under components.schemas.EditRequest. How a request is applied says how edits
in one request combine.
| Operation | Changes | Names | Tracked |
|---|---|---|---|
insert | new text, breaks, fields, cross-references, notes and links at a point | paragraphs and a point | yes; a cross-reference's bookmark, a new notes part and note styles are not |
replace, delete | text, or a page break | paragraphs and a selector | yes |
format | character formatting | paragraphs, and optionally a selector | yes |
insert_paragraphs | new paragraphs | a destination | yes; a new list's definitions are not |
delete_paragraphs | whole paragraphs, and tables a range covers | paragraphs | yes |
move_paragraphs | where paragraphs and tables are | paragraphs and a destination | yes |
split_paragraph | one paragraph into two | a paragraph and a point | yes |
join_paragraphs | a range of paragraphs into one | a range | yes |
set_paragraph_style | paragraph style | paragraphs | yes |
set_paragraph_format | alignment, indents, spacing, pagination | paragraphs | yes |
set_style_format | a paragraph style's alignment, indents, spacing, pagination | a style | no: a style definition |
rename_style | a style's name and id | a style | no: a style definition |
set_numbering | list numbering | paragraphs | yes; new list instances and definitions are not |
insert_section_break | a new section break | the paragraph that ends the section | yes |
remove_section_break | a section break | a section | yes; with keep: "earlier", the next section's header and footer references are not |
set_page_setup | orientation, paper, margins, first page, page numbering | sections | yes |
add_header_footer | a new header or footer | a section | its content; the new part and its reference are not |
insert_table | a new table | a destination | yes |
delete_table | a table | a table | yes |
insert_table_row, delete_table_row | a row | a row | yes |
insert_table_column, delete_table_column | a column | a column | yes |
set_table_format, set_cell_format | a table's or cells' borders | a table, or cells | yes |
add_hyperlink | a link on existing text | a paragraph, and optionally a selector | yes |
set_hyperlink, remove_hyperlink | a link's address, or the link | a paragraph, and optionally a selector | yes |
add_bookmark | a named span fields and cross-references point to | paragraphs, or a paragraph and a selector | no: a bookmark is not a revision |
refresh_fields | when Word updates fields | nothing | no |
insert_table_of_contents | a new table of contents | a destination | yes; its update-on-open flags are not |
delete_note | a footnote or endnote | a note | yes |
add_comment | a new comment | a paragraph or range, and optionally a selector; or a revision | no: a comment is an annotation |
reply_comment, resolve_comment | a comment thread | a comment | no |
accept_revision, reject_revision | existing tracked changes | revisions | settles them |
set_document_settings | Track Changes, editing restriction, personal information | nothing | no |
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_fieldsandinsert_table_of_contents: fields'w:dirtyflags andw:updateFieldsin the settings. The table of contents' paragraph is tracked.- Comments from
add_comment,reply_commentandresolve_comment: annotations, not revisions. - New list instances (
w:num) and definitions (w:abstractNum), andnumbering.xmlwhen the document has none, thatset_numberingandinsert_paragraphslists need. The paragraphs that use them change as tracked changes. - A
cross_referencerun: the hidden_Refbookmark on the paragraph pointed to, when it has none. - A
footnoteorendnoterun: the notes part, with Word's separator notes, and the note styles, when the document lacks them. add_hyperlinkandhyperlinkruns: 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.remove_section_breakwithkeep: "earlier": the header and footer references the next section takes from the removed one. The break and the next section's page setup are tracked.set_style_format: the paragraph style's definition (w:style/w:pPr), as Word applies a modified style.rename_style: the style's name and id and every reference to its id, as Word renames a style.
Locators
Everything an edit names is a locator object, never a bare number. The listing prints every locator
(The content listing), so agents copy locators instead of computing them. A locator names
its target by index, where it is now, counted from 0 within its container, or by id, what it is, which it keeps
when content moves; everything the listing identifies has both (paragraphs, tables, rows, cells, sections, comment
threads, notes, headers, footers, pictures and revisions), and a locator may give both when they name the same thing
(otherwise it is refused). Beside an id, an index needs no container: it counts within the container of the
thing the id names, so a header's paragraph, a row or a cell printed as {"index", "id"} is sent as printed. Every
reference the listing prints is a locator a request can send as it is.
Paragraph locators
{"index": 12} // body paragraph 12
{"id": "8532bf246ab64e8788c26d27cfd105b4"} // the paragraph with this id, in any part
{"index": 12, "id": "8532bf246ab64e8788c26d27cfd105b4"} // both, naming the same paragraph
{"part": {"kind": "footnote", "index": 2}, "index": 0} // first paragraph of footnote 2
{"part": {"kind": "header", "type": "first", "section": {"index": 1}}, "index": 0}
{"part": {"ref": "fn1"}, "index": 0} // a note or header this request creates
{"ref": "p1"} // a paragraph this request createsindex. It counts the paragraphs of a part from 0, in listing order, including paragraphs in table cells.partgoes withindex; anidnames its paragraph in whatever part it is.- Tracked text. The document is seen as the listing 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.
kindisbody(the default whenpartis omitted),footnote,endnote,headerorfooter.- A note's
indexis 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 listing prints that aslabel. - A header or footer has a
type(default,firstoreven) and asectionlocator. Together they name the part that section's pages show. - A part that several sections show through Word's linking is one part. The listing 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 listing's
sectionssays which. - A section that
insert_section_breakcreates 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.
- An id that names more than one element. An id names one paragraph, table, row or cell. When a document
gives one id to more than one of them (a comparison's redline can repeat a header's or footer's paragraph ids, in
the parts it shows and in a part no section shows), an edit that names one of them is refused with 400
INVALID_EDIT, which lists every place the id is. This holds however the edit names it: byid, byindex, or through"all", a range or a selector's match. Content whose ids nothing else carries is edited as usual. - 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
| Names | Locator |
|---|---|
| a section | {"index": 1} or {"id": "…"}; set_page_setup also takes an array of section locators, or "all" |
| a table | {"index": 1}: 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. Or {"id": "…"}, in any part. |
| a row | {"table": {...}, "index": 3} among its table's rows, {"id": "…"}, or {"paragraph": locator}, the row holding that paragraph in the innermost table holding it |
| a column | {"table": {...}, "index": 2}, counting the table's grid columns from 0 (a cell's column in the listing, never its index), or {"paragraph": locator}, the column of the cell holding that paragraph; a paragraph in a cell that spans several grid columns can't name a column |
| a cell | {"row": {...}, "index": 1} among its row's cells, {"id": "…"}, or {"paragraph": locator} |
| a comment thread | {"index": 2}: top-level threads in document order, or {"id": "…"}. Replies aren't counted, since replying and resolving act on a thread. |
| a note | {"kind": "footnote", "index": 2}, or {"kind": "footnote", "id": "…"} |
| a header or footer | {"kind": "header", "type": "first", "section": {"index": 1}}, {"kind": "header", "index": 0} (the headers in order of the first section that shows each), or {"kind": "header", "id": "…"} |
| revisions | {"index": 4} or {"id": "…"}, one entry of the listing's revisions; "all"; or a filter {"author"?: "…", "type"?: "insertion" | "deletion" | "move" | "property"}, where property covers every formatting, paragraph, numbering, table and section property change |
Any locator but a cell's may instead be {"ref": "…"}, which names something this request creates
(Refs).
Counting
Every index counts from 0, because it is a position in the listing's arrays: paragraphs, tables, rows, cells,
grid columns, sections, notes, comment threads, pictures and revisions. What counts from 1 counts as a reader does:
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.
paragraphs
Every operation that acts on paragraphs names them in one required field, paragraphs:
| Form | Names |
|---|---|
| a locator | that paragraph |
{"from": locator, "through": locator} | the uploaded paragraphs between the two, inclusive, in uploaded order, within one part. A range of refs from one insert_paragraphs names those new paragraphs. |
| an array of locators and ranges | each 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
replacesays"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_paragraphsorjoin_paragraphsthat 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_hyperlinkandremove_hyperlinktake exactly one locator, since a Word hyperlink can't cross paragraphs.add_commenttakes one locator or one range; a range makes one comment spanning it.join_paragraphstakes 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_tablethere makes a nested table. {"table": locator, "position": "before" | "after"}places content directly before or after a table.{"position": "start" | "end"}, naming no paragraph or table, places content at the start of the body, before its first block, or at its end, after its last block.startandendtake no paragraph or table, andbeforeandafterneed one. Content placed at the start comes in request order; content placed at the end goes after what earlier edits placed there. A table placed at the end gets the empty paragraph Word needs after it, and content placed at the end later goes before that paragraph and, when it ends in a paragraph, takes its place. A document with no paragraph to name, such as the blank documentPOST /v1/createstarts from, takes its first content here.- Content placed after one paragraph or table comes before content placed before the next one.
insert_table_rowuses{"row": locator, "position": …}, andinsert_table_columnuses{"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 by | Names |
|---|---|
new_paragraphs items | the new paragraph |
split_paragraph | the second half |
insert_section_break | the new section after the break; the original {"index": n} still names the section before it |
insert_table, insert_table_row, insert_table_column | the new table, row or column |
add_header_footer | the new part: {"part": {"ref": "hdr1"}, "index": 0} |
| note runs | the new note: {"part": {"ref": "fn1"}, "index": 0} |
add_comment | the 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
matchstring,\tmatches a tab run and\na 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
matchis a string or an array of runs. The run form is for content with no characters:[{"type": "break", "kind": "page"}]matches a page break and[{"type": "picture", "index": n}](or"id": "…", or both) the listing's picturen, fordeleteonly, withoutprefixorsuffix. The picture must be in the named paragraphs, and not in a tracked deletion or a move's old place. prefixandsuffix. 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 listing order: the body; for each section in turn, its header then its footer, each in the orderdefault,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, e.g. 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_hyperlinkandsplit_paragraphmust select exactly one range or point.- So must an
insertwhosecontentholds a note run with aref. split_paragraphat"start"or"end"is refused, since it would make an empty paragraph. Useinsert_paragraphsfor that.add_hyperlinkover text that is already linked is refused. Useset_hyperlink.
- Required or optional.
deleteandreplacerequiretext.format,add_commentandadd_hyperlinkwithouttextact on the whole paragraph.set_hyperlinkandremove_hyperlinkwithouttextact 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 listing. 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, pictures and objects are skipped by matching, so
"409A."matches across a footnote mark between "409A" and ".". Areplaceordeletewhose 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 listing shows it, and can anchor a
selector, but a
replace,deleteorformatrange 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 aREFresult, removes the field and is accepted. AHYPERLINKfield'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 listing's shapes.
| Run | Inserts |
|---|---|
{"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 |
{"type": "picture", "source": {"file": "…"} | {"data": "…"}, "width"?, "height"?, "alt_text"?} | a picture in line with the text (Pictures) |
- Any run can carry
format(Property values). A note run can carry aref. - In a
contentstring or a text run,\tbecomes a tab run and\na line break. New paragraphs come only frominsert_paragraphs,split_paragraphand thenew_paragraphsforms. - A table cell's value is
content(one paragraph) or{"new_paragraphs": […]}, whose items areinsert_paragraphsitems. - A comment's
content(add_comment,reply_comment) is one paragraph: a string, or text, tab and line-break runs. Other runs are refused. - A note run's content holds no picture or note. A hyperlink's content holds text, tab and break runs.
replacerequires non-emptycontent. Removing text is adelete.- 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 plaincontenttakes 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.
Pictures
A picture run inserts an image in line with the text, as a tracked insertion, wherever content goes:
insert, replace, insert_paragraphs items, table cells, add_header_footer, and paragraphs of a header,
footer or note. A comment and a note run hold no picture.
{"type": "insert", "paragraphs": {"index": 0}, "at": "start",
"content": [{"type": "picture", "source": {"file": "logo.png"}, "width": 144, "alt_text": "Acme logo"}]}| Field | |
|---|---|
source | {"file": name}, the file name of a pictures file uploaded with the request (multipart), or {"data": base64}, the image's bytes in base64, which count toward the 200 KB of edits. Exactly one. A request sent as JSON alone gives data. |
width, height | Optional, in points, above 0 and at most 1584 (22 inches). With one, the other keeps the image's proportions; with neither, the picture takes the image's own size at its resolution (96 dpi when the file states none), made smaller to fit the text or cell width. An explicit size is not fitted. |
alt_text | Optional: the picture's alternative text. |
format | Optional character format of the picture's run. |
An image is a PNG, JPEG or GIF, its type read from its bytes rather than its file name. Linked (external)
images are not supported. Each image is at most 5 MB and 10,000 pixels on a side; a request inserts at most
20 different images, 25 MB in all (the same image used twice counts once), and uploads at most 20 files.
Each of these is refused at request time with INVALID_EDIT, together with the request's other refusals:
an unsupported or corrupt image, one past a limit, source with both or neither of file and data,
data that is not base64, a file no upload has, an upload no picture run names, two uploads with the
same file name, width or height at or below 0 or above 1584, and a picture in a comment or note run.
curl -sS -X POST "https://api.versionstory.com/v1/edit" -H "Authorization: Bearer vs_live_..." \
-F file_id=3f2a… -F schema_version=1 -F pictures=@logo.png \
-F 'edits=[{"type": "insert", "paragraphs": {"index": 0}, "at": "start",
"content": [{"type": "picture", "source": {"file": "logo.png"}}]}]'A listed picture is deleted with delete and its run-form match:
{"type": "delete", "paragraphs": {"index": 3}, "text": {"match": [{"type": "picture", "index": 1}]}}.
Naming rules
- Reserved keys.
type,textandatare reserved among an edit's keys. Inside a run,typeis the run's type andtextits characters, as in the listing. - Property objects. As a key,
formatalways holds a character-format object. Paragraph properties nest underparagraph_formatand page properties underpage_setup, the same objects the listing returns. - Field names. Styles are named by
style_id.insert_paragraphstakesnew_paragraphs, becauseparagraphsis a locator field. - Operation names.
insert_*places new content at a position, andadd_*attaches something to existing content.delete*removes content, andremove_*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:
| Value | Effect |
|---|---|
| a value | sets the property |
false | turns it off, even where the style turns it on |
null | removes the direct setting, so the style decides |
| key omitted | leaves the property alone |
- Toggles.
bold,italic,small_caps,keep_with_nextand the like taketrue,falseornull. - Enums with an off state.
underline,highlightandstriketake an enum value,falsefor off, ornull.truemeans"single"forunderlineandstrike; it is refused forhighlight. - Everything else. Other properties take a value or
null. - Page setup. A section has no style for
nullto fall back to, so inpage_setuponlydifferent_first_pageandpage_numbering(and its fields) takenull. - Nested objects merge field by field.
- For example,
{"indent": {"left": 108}}keeps the paragraph's other direct indent values. - Setting
first_lineremoveshanging, and settinghangingremovesfirst_line. nullfor a whole object ("indent": null) removes all of its direct values.- The same merge applies to
margins,spacing(which holdsbefore,afterandline_spacing),page_numberingandsettings. line_spacingis one value in one of three forms ({"multiple": 1.15},{"exact": 14}or{"at_least": 14}), so it is replaced whole.
- For example,
- Whole paragraphs. A
formatwithouttextformats the paragraph mark too, as selecting a whole paragraph in Word does. - Removing everywhere. To "remove X everywhere", send
false.nullonly removes direct formatting, so a heading that is bold through its style would stay bold. - Units. Indents, spacing, margins, paper size and
font_sizeare in points.
How a request is applied
Three rules:
- Addresses mean the document as you read it. Every locator and selector refers to the uploaded document, the one the listing described, whatever earlier edits in the request have done.
- Edits apply in request order. When two edits set the same property of the same text or paragraph, the later one wins.
- 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 listing the agent read. The request is all or nothing, and revision decisions go alone (see Editing documents).
Addresses
- Indexes don't shift. If edit 1 inserts a paragraph after paragraph 5, edit 2's
{"index": 10}still means the paragraph the listing called 10, now eleventh in the document. Anidnames the same thing it named in the listing, wherever earlier edits moved it. - Table rows and columns don't shift.
{"table": {"index": 1}, "index": 3}is the listing's row 3 (or grid column 3) whatever earlier edits inserted. A new row's or column'scellscount the table as the earlier edits left it (insert_table_row). - 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,formatwithouttext,add_commentwithouttext,insertat"start"or"end", and as adestinationor cross-referenceto. Its text can't be selected, since it isn't in the listing; 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.
- Moves and other edits to the same paragraphs. A
move_paragraphscan come before or after edits to the paragraphs it moves (replace,format,set_paragraph_style, a comment, a hyperlink, ...); the result is the same either way, with the edits' changes inside the moved paragraphs. - 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_paragraphsmerged 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_templatestyles 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_fieldsdoes the same for existing fields. Results that need page layout are those the field tables below say the server can compute; any others are marked for Word to update on open. - Cross-references stay current. After the last edit, every
REFfield in the document (body, headers, footers and notes) is recomputed; see Cross-references after edits.
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
replaceputs its new text at the start of its range, so aninsertat 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.
- A
- 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
replaceinside 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_revisionwith"all", thenaccept_revisionwith{"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 it
is placed beside (its destination
row), and a new column copies its destinationcolumn.
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 characters | replaced or deleted them |
| has an insertion point strictly inside a range | replaced or deleted that range |
| names a paragraph for a paragraph-level edit, or as a destination | merged it away with join_paragraphs, or deleted it (except as a destination, above) |
| names a table, row, column, section or note | deleted it, or removed the section's break |
has an insertion point at a split point, or a replace or delete crossing one | split the paragraph there |
| splits inside a range | replaced or deleted that range |
| names a header or footer through a section that now shows a different part | added 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.
| Combination | Why |
|---|---|
A replace, delete, insert or format inside a field result | Updating the field would undo it. Edit the field's target instead. |
Two add_hyperlink ranges that overlap | Word can't nest hyperlinks. |
A move_paragraphs whose destination is inside the paragraphs it moves | The destination moves with them. |
| A row insert inside a vertically merged span, or a column insert or delete through a cell spanning several grid columns | The grid can't keep or split the merge. |
| Selecting text in a paragraph this request creates | It isn't in the listing. Describe it in the creating edit. |
A ref on something an edit would create more than once | It 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 property | Values |
|---|---|
bold, italic, small_caps, all_caps, superscript, subscript | true, false or null |
underline | true (single), single, double, thick, dotted, dash, wave, words, false or null |
strike | true (single), single, double, false or null |
highlight | a Word highlight color (yellow, green, cyan, magenta, blue, red, black, white, dark_blue, dark_cyan, dark_green, dark_magenta, dark_red, dark_yellow, dark_gray, light_gray), false or null |
color | six hex digits (1F3864), or null |
font_size | points, in halves (10.5), or null |
font | a font family name, or null |
At least one property is required, and superscript and subscript cannot both be true. The listing
reads formatting back as this object, which an edit takes unchanged: each text segment's format holds the
properties above that the text has through its styles, in these values ("strike": "double" for a double
strike); color, font_size and font appear where they differ from the document's default text formatting.
An underline style no edit sets, such as a dot-dash one, is left out.
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 or"", an empty paragraph) and, optionally,style_id,paragraph_format,list,list_level(1 to 9) andref. - Operation-level values.
style_id,paragraph_format,listandlist_levelon the edit apply to items that don't set their own; an item'sparagraph_formatmerges 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": nullleaves it out of the operation's list, so its style's numbering, if any, applies;falsegives it no numbering.list_leveldefaults to the operation's, else thesame_asparagraph's, else 1. A paragraph whose style already numbers it in that list at that level, as a heading style numbers a heading, joins it through its style, with no numbering of its own, so it keeps the style's indents as the list's other paragraphs do; otherwise the paragraph names the list and level itself and takes the level's indents.- Typography. A new paragraph in the style of the destination paragraph continues that
paragraph's character formatting, as pressing Enter in Word does: its text takes the destination's
paragraph mark formatting, with each run's
formatover it. A paragraph in another style takes the formatting most of the text of the nearest paragraph of its own style has (in the same part and at the same level, body or table cell, before the destination, else after it), so it reads as the document's other paragraphs of that style do where a template sets their size or font on the text rather than in the style; with none, it starts from its style alone. A new paragraph's mark, which sets the size and font of a list number, takes the formatting its text shares; an empty paragraph's mark takes the one it continued.
A destination is a paragraph directly in the body or a table cell. A missing paragraph style fails the
job 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}]}The last paragraph of the document, a cell, a header, a footer or a note holds the mark Word requires there, so deleting it deletes its text and the mark of the paragraph before it instead, and that mark's properties pass to the last one as tracked changes: accepting leaves the paragraph before it last, as it was, and rejecting restores both.
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 only paragraph of the document, a header, a footer or a note, and
a last paragraph after a table or content control, or after a paragraph that ends a section (delete its
text with delete); a paragraph that ends a section; paragraphs that are all that keeps
two tables apart, since Word joins tables that touch; and a paragraph holding a bookmark that a REF or
PAGEREF field in another paragraph points to, unless the request deletes that paragraph too or a replace or
delete removes the field (the refusal names the referencing paragraphs; see
Cross-references after edits).
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.
pending_changes is optional: "accept" or "reject" settles the uploaded document's tracked changes
inside the moved paragraphs (and their notes) before the move, so the move carries clean content.
"accept" keeps insertions and drops deletions; "reject" drops insertions and restores deletions. Without
it, a move of paragraphs that hold tracked changes is refused, and the refusal lists those changes. It
settles only the changes inside the moved paragraphs; changes elsewhere are left as they are.
{"type": "move_paragraphs", "paragraphs": {"index": 9}, "pending_changes": "accept",
"destination": {"paragraph": {"index": 19}, "position": "after"}}A move does not depend on where it sits in the request relative to other edits to the same paragraphs:
replace, format, set_paragraph_style, add_comment and the like give the same document before or
after the move.
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 (unless pending_changes is given), 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 job 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 job 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}}| Property | Values |
|---|---|
alignment | left, center, right, justify, or null |
indent | left and right (points, -1584 to 1584), and first_line or hanging (0 to 1584) |
spacing | before and after (points, 0 to 1584), line_spacing |
keep_with_next, keep_lines_together, page_break_before, widow_control | true, false or null |
borders | by side (top, left, bottom, right, between, bar), each a border 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. Each listed paragraph's paragraph_format is this object,
which this edit takes unchanged, where the paragraph sets a property or differs from the document's default.
set_style_format
set_style_format sets a paragraph style's own formatting, which every paragraph in the style, and in the
styles based on it, takes: keep with next on the section heading style keeps each heading on the page of
the clause below it.
{"type": "set_style_format", "style_id": "Heading3", "paragraph_format": {"keep_with_next": true}}paragraph_format takes set_paragraph_format's properties; null removes the style's own setting, so
it inherits the setting of the style it is based on. Paragraphs that set a property themselves keep their
own value. The change is applied to the style definition untracked, as Word applies a modified style
(see Tracked and untracked). The listing's paragraph_styles gives each
paragraph style in use, and the styles they are based on, with its paragraph_format in these terms. A
style the document does not define, or one that is not a paragraph style, fails the job with
INVALID_EDIT.
rename_style
rename_style gives a style a new name, as Word's Modify Style › Name does. Word matches styles by name
when text comes in from another document, through an INCLUDETEXT field or a paste: a style whose name the
other document also uses takes that document's definition there, so a DPA's "heading 2" included in an
agreement whose "heading 2" is centred and bold becomes centred and bold. Under a name of its own, the
style comes in with its own definition and the text keeps its formatting wherever it goes.
{"type": "rename_style", "style_id": "Heading2", "name": "DPA Heading 2"}The style's new style_id is the name's letters and digits (DPAHeading2, numbered on when another style
has it), and everything that named the old id follows: the paragraphs, runs and tables in the style in every
part, including the earlier properties tracked changes hold, the styles based on it, following it or linked
to it, and the list levels it numbers. Its linked character style, named "Heading 2 Char" as Word names one,
becomes "DPA Heading 2 Char". A built-in heading's outline level, which Word gives "heading 1" to "heading
9" by name, is written into the style, so the renamed heading stays in the navigation pane and tables of
contents. Later edits in the request name the style by its new id. The rename is applied untracked, as Word
applies a modified style (see Tracked and untracked).
Refused with INVALID_EDIT: a style the document does not define; the document's default style of its type
(Normal), which text without a style is in whatever its name; a name another style or one of Word's
built-in styles has, whatever its case; a name with a comma, which Word reads as separating a style's names;
and a style a STYLEREF or table of contents field names, since it would stop finding the style.
set_numbering
set_numbering changes list numbering by action. list_level counts from 1, as the listing's list_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"}, "list_level": 1}
{"type": "set_numbering", "paragraphs": {"index": 21}, "action": "set_level", "list_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", "list_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 asame_asparagraph. Each keeps its level (1 if it had none) unlesslist_levelis given.restartandcontinue. At the first named paragraph,restartnumbers fromstart, andcontinueresumes 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'snumber_format(decimal,decimal_zero,lower_roman,upper_roman,lower_letter,upper_letter,ordinal,cardinal_text,ordinal_text,bullet,none) and optionalpattern((%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 job 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
listing shows each numbered paragraph's label and list_level, 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
listing shows them as that paragraph's section, and the last section's as final_section, each with its
section_type and its page_setup in set_page_setup's terms. 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. A paragraph an earlier edit of the request
inserts may be named by its ref, so a request can add content and start a new section after it (an
exhibit or schedule on a new page). 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 index 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. A paragraph the request inserts carries the section's
properties on its own mark, part of that paragraph's insertion: rejecting it removes the paragraph and
its break together.
remove_section_break
remove_section_break removes the break that ends section, joining its content to the next section.
keep says whose properties the merged section has:
"earlier"(the default):section's own headers, footers (every type), page setup, columns, page numbering, different first page and section type. The next section's settings as they were are a trackedw:sectPrChange; its header and footer references change untracked, since aw:sectPrChangecannot hold them, so rejecting restores the break and the next section's page setup but leaves that section withsection's headers and footers."later": the next section's properties, headers and footers, as Word does. The merged section starts as the removed one started (its section type and page-number restart), a trackedw:sectPrChange.
{"type": "remove_section_break", "section": {"index": 1}}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. Removing several breaks in one request gives the same clean copy in either order. 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"}}}| Field | |
|---|---|
orientation | portrait or landscape |
paper | letter, legal or a4; or page_width and page_height (points, 72 to 1584) instead |
margins | points by side: top, bottom, left, right, header, footer, gutter |
different_first_page | true gives the first page its own header and footer (w:titlePg) |
page_numbering | start (0 to 32767) and number_format (decimal, lower_roman, upper_roman, lower_letter, upper_letter) |
columns | count (1 to 45) columns of equal width, and spacing between them in points (Word's 36 when the section sets none); the listing's page_setup shows a section's columns, in these terms, when its text runs in more than one of equal width |
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. The listing prints each section's page_setup as this object, which
this edit takes unchanged.
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); with neither, the part is
one empty paragraph, a blank header or footer, and an item without content is an empty paragraph. ref
names the new part.
{"type": "add_header_footer", "part": {"kind": "footer", "type": "default", "section": {"index": 0}}, "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.
A section without its own reference of a kind and type shows the previous section's part (Word's "Link to
Previous"), and the two sections insert_section_break makes name the same parts, so one part can show in
several sections. Adding to a section whose part another section also shows gives that section alone a new
part; every other section keeps what it showed, the next section getting its own reference to the old part
when it was linked to this one. When the section showed no part of the kind and type, later sections 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 whose part of that kind and type no other section shows, which is
its own (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 listing shows a table's index, id and style_id, each row's index, id, header flag and tracked
change, and each cell's index, id, 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,
list_level, ref).
insert_table
insert_table places a table at a destination. rows holds each row's cell values, every row spanning the
same number of grid columns (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. A cell of several
paragraphs may give span, the grid columns it is merged across ({"new_paragraphs": [...], "span": 2}, as
the listing gives a merged cell's span; 1 when left out): it counts as that many columns of its row.
insert_table_row and insert_table_column copy the cells beside them, so their cells take no span.
{"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"}]}, "—"],
[{"new_paragraphs": [{"content": "Total authorized"}], "span": 2}]]}A cell value of "" is an empty cell: a paragraph with no text. The same holds for the cells of
insert_table_row and insert_table_column. Everywhere else, empty text is refused.
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": {"index": 2}}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 cell of row, the row it copies,
as the listing shows them (a cell spanning two grid columns is one value); ref names
it. delete_table_row deletes a row as a tracked row deletion (w:trPr/w:del) with its text deleted.
Rows and columns of one table can be inserted and deleted in one request, and each new row or column counts its values against the table as the earlier edits of the request left it, the same as sending the edits in turn. Locators still give the listing's row and column indexes.
- A row copies every cell of
rowas it is then: a column an earlier edit inserted gives it one more cell, and one more value. - A column covers every row as it is then: a row an earlier edit inserted gets a cell in it, and one more value.
- A cell with a tracked deletion (
changeof typedeletionin the listing, or a column an earlier edit deleted) is copied as an empty cell and takes no value. A row with a tracked deletion (or one an earlier edit deleted) gets an empty cell in a new column and takes no value.
{"type": "insert_table_column", "destination": {"column": {"table": {"index": 1}, "index": 2}, "position": "after"},
"cells": ["Integrated since", "March 15, 2021", "September 12, 2022"]}
{"type": "insert_table_row", "destination": {"row": {"table": {"index": 1}, "index": 2}, "position": "after"},
"cells": ["Datadog, Inc.", "Sarah Mitchell", "Application performance monitoring", "June 3, 2024"]}In the other order, the row takes three values and the column four.
{"type": "insert_table_row", "destination": {"row": {"paragraph": {"index": 311}}, "position": "after"},
"ref": "google", "cells": ["Google LLC", "Google Cloud Support", "AI-Enhanced PDF Conversion"]}
{"type": "delete_table_row", "row": {"table": {"index": 0}, "index": 3}}
{"type": "delete_table_row", "row": {"id": "6e127ae6b2e144759046e4f3f1fe01de"}}The new row copies the cell layout of the row it is placed beside and formatting, without its revisions, vertical
merges or header flag (the row it is placed beside), 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 number of cells in the row it copies (a merged cell counts once, a
deleted cell not at all).
insert_table_column and delete_table_column
insert_table_column adds a grid column before or after column, with one cell value per cell of column,
the column it copies (one per row; a row whose cells stop short of the new column, or one with a tracked
deletion, takes none);
delete_table_column deletes a column.
{"type": "insert_table_column", "destination": {"column": {"table": {"index": 1}, "index": 2}, "position": "after"},
"cells": ["Q3", "12", "9"]}
{"type": "delete_table_column", "column": {"paragraph": {"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.
The table keeps its width. The new column starts as wide as the column it is placed beside, then all the
columns, the new one included, shrink by the same proportion until the grid is as wide as it was. A
table 9630 twips wide with columns of 2790, 3397 and 3443 gets a fourth column after the third of 2537,
the others becoming 2055, 2502 and 2536. The table's own width (w:tblW, in twips, a percentage or auto)
is not changed. Each cell's width follows the columns it spans, as a tracked property change
(w:tcPrChange) that keeps its earlier width; rejecting restores the grid and the widths.
A deleted column's cells are tracked cell deletions (w:cellDel) with their text deleted; accepting gives
each cell's grid column and width to the cell before it, so the table keeps its width (a deleted first
column has no cell before it, and leaves its rows a column short). Refused: a column through a cell spanning several grid columns, a cell
count other than the column's cells, and a paragraph locator in a spanning cell, which names no one column.
set_table_format and set_cell_format
set_table_format sets a table's borders; set_cell_format sets the borders of cells: one cell locator
({"row": {...}, "index": n}, {"id": "…"} or {"paragraph": locator}), or {"from": cell, "through": cell},
every cell of one table whose row lies between the two cells' rows and whose first grid column lies between the
grid columns they start at.
{"type": "set_table_format", "table": {"index": 0},
"table_format": {"borders": {"inside_horizontal": {"style": "single", "width": 0.5, "color": "auto"}, "left": null}}}
{"type": "set_cell_format", "cells": {"row": {"table": {"index": 0}, "index": 1}, "index": 3},
"cell_format": {"borders": {"bottom": {"style": "dotted", "width": 0.5}}}}
{"type": "set_cell_format",
"cells": {"from": {"row": {"table": {"index": 0}, "index": 2}, "index": 1},
"through": {"row": {"table": {"index": 0}, "index": 2}, "index": 3}},
"cell_format": {"borders": {"bottom": {"style": "none"}}}}A table's sides are top, left, bottom, right, inside_horizontal (between rows) and
inside_vertical (between columns); a cell's are the same, plus diagonal_down and diagonal_up, and a
paragraph's (set_paragraph_format) are top, left, bottom, right, between and bar.
| Border field | Values |
|---|---|
style | none, single, thick, double, dotted, dashed, dot_dash, dot_dot_dash, triple, wave, double_wave, dash_small_gap, dash_dot_stroked, three_d_emboss, three_d_engrave, outset, inset and the thin-thick gaps (thin_thick_small_gap, ... thin_thick_thin_large_gap) |
width | points, 0.25 to 12 in eighths; 0.5 when left out |
color | six hex digits, or auto (the default) |
theme_color | a theme colour (text1, accent1, ...), which Word prefers to color |
space | points between the border and the text, 0 to 31 |
shadow, frame | true or false |
A side given a border is set to it; null removes the side's own setting, so the table's or style's
applies; a side left out is kept. Style none is an explicit absence (w:val="nil"): it keeps a border
the table, its style or a neighbouring cell would draw from being drawn, and takes no other field. Where
two cells meet, Word draws the border either sets, so removing a line between two rows means the upper
cells' bottom and the lower cells' top. The change is tracked: the table's previous properties go
into a w:tblPrChange, each cell's into a w:tcPrChange, so rejecting restores them; a format the table
or cell already has records nothing. The listing reports each table's table_format, each cell's
cell_format and each paragraph's paragraph_format.borders, the borders they set themselves, in these
terms. Refused: a cell no row has at that row and column, and a table this request creates.
Fields and links
Field and cross-reference runs
A field run inserts a document field by its code, Word's field code, as the listing's fields print
it; a cross_reference run inserts a field pointing to another paragraph, to.
{"type": "insert", "paragraphs": {"part": {"kind": "footer", "type": "default", "section": {"index": 0}}, "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 code | Result Version Story writes |
|---|---|
PAGE, NUMPAGES, SECTIONPAGES | 1, which Word replaces when it lays out the pages |
DATE | the edit's date; Word shows the current date on open |
CREATEDATE, SAVEDATE | the document's created or last-saved date |
TITLE, AUTHOR | the document's title or author |
DOCPROPERTY | the named property: a custom property, or Title, Subject, Author, Keywords, Comments, Category, Company, Manager, LastSavedBy |
FILENAME | none; marked for Word to compute on open |
INCLUDETEXT "file" [bookmark] | the run's result, such as "[Exhibit A loads here: select all and press F9]", until the reader updates the field |
A field is one tracked insertion: begin, instruction, result and end inside a w:ins, in the formatting
of the text before it. INCLUDETEXT shows another Word file's text, or one bookmark of it, once the
reader updates the field in Word: its code names the file in quotes, as the reader's computer finds it,
and optionally a bookmark, with \* MERGEFORMAT the only switch. Version Story cannot read that file, so the
field shows the run's result (1 to 1000 characters; none, an empty result) and is never marked to update
on open; only an INCLUDETEXT run takes a result. No other field that fetches or runs content
(INCLUDE, INCLUDEPICTURE, DDE, DDEAUTO, IMPORT, LINK, MACROBUTTON, …) is offered, and
refresh_fields still refuses a document that holds one, INCLUDETEXT included.
{"type": "insert_paragraphs", "destination": {"paragraph": {"index": 160}, "position": "after"},
"new_paragraphs": [{"content": [{"type": "field", "code": "INCLUDETEXT \"/Templates/DPA.docx\" DPABody",
"result": "[The DPA loads here. Select all and press F9.]"}]}]}show | Field | Result |
|---|---|---|
number | REF _Ref… \r \h | the paragraph's list number, without trailing periods (2.1) |
text | REF _Ref… \h | the paragraph's text |
page | PAGEREF _Ref… \h | 1, marked for Word to compute on open |
above_below | REF _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
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 and remove_hyperlink
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 listing shows a link on its
segments as "link": {"url": …, "anchor": …}, and each paragraph's fields and bookmarks.
add_bookmark
add_bookmark names a span of the document, as Word's Insert › Bookmark does: whole paragraphs, from the
start of the first to the end of the last, or one text match in a paragraph. A field or cross-reference
names the span by it, such as an INCLUDETEXT field in another document that includes only that part of
this one (Field and cross-reference runs).
{"type": "add_bookmark", "paragraphs": {"from": {"index": 2}, "through": {"index": 331}}, "name": "DPABody"}
{"type": "add_bookmark", "paragraphs": {"index": 14}, "text": {"match": "Effective Date"}, "name": "EffectiveDate"}name is a letter, then letters, digits and underscores, 40 at most. Refused: a name starting with an
underscore, which Word keeps for the bookmarks it hides (_Ref, _Toc), and a name the document already
uses, whatever its case, since Word keeps one bookmark of each name. A bookmark is not a revision: it is
added untracked, as Word adds one with changes tracked, and the listing shows it among the bookmarks of the
paragraph where it starts.
Cross-references after edits
After the last edit, the server recomputes every REF field in the body, headers, footers and notes, so a
cross-reference shows the number or text its target has in the edited document: a paragraph that was
renumbered by an insert, a heading whose text was replaced, a clause moved elsewhere. A field whose result
changes is updated as a tracked change (the old result deleted, the new one inserted, with the request's
author and date), so rejecting restores the old result.
REFfields only are recomputed. APAGEREFor other page-dependent field is only marked for Word to update on open (w:dirty), since the page depends on layout.- Plain-text references, such as "see Section 2.1" typed as text, are not fields and are left untouched.
- A
REForPAGEREFwhose target paragraph an edit deletes is refused, and so is the request; a field inside another field is left alone. The backend checksdelete_paragraphsat request time from the listing (each paragraph'sfieldsandbookmarks) and names the referencing paragraphs; edit or remove those references in the same request (areplaceof the reference text removes the field, or delete the referencing paragraph), or keep the target. Ajoin_paragraphskeeps the joined paragraphs' text and bookmarks, so it never removes a target. A reference in a footnote or endnote the request removes (adelete_note, or adelete_paragraphsof the paragraph holding the note's reference) goes with the note. Where areplaceordeletein a paragraph removes a field, the backend leaves that paragraph to the edit as it is applied, against the edited document. A deletion of the target that the listing cannot show, such as one inside a table deleted as a whole, fails the edit withINVALID_EDIT.
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. Version Story does not 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", "index": 1}}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 listing prints 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, to
a range of paragraphs, or, with revision instead of paragraphs, to a tracked revision the listing's
revisions gives by index. 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."}
{"type": "add_comment", "revision": {"index": 3}, "content": "Changed at the client's request."}A comment on a revision covers what the revision changes: the text it inserts, deletes or moves or whose
formatting it changes, through the end of a paragraph whose mark it changes; the whole paragraph whose
properties or numbering it changes; and the whole table, row or cell it changes. A move's comment is at
its destination. A revision of paragraph marks alone (a split or a join) covers those paragraphs. One
that runs from the text around a text box into the box covers that text, the box included. Its
reference is outside the revision, so accepting or rejecting the revision keeps the comment. A revision
in a header, footer or note, or one that changes only the last section's page setup, is refused. To
annotate a comparison's redline, read its revisions from the json listing of the comparison's file.file_id
(GET /v1/files/{file_id}?format=json), send one
add_comment per revision, and download the result's files.comparison, which keeps the tracked
changes beside the comments.
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
index from the listing'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": {"index": 2}}]Replies and resolutions are annotations, applied untracked. Replies to one thread appear in request
order. The listing gives each thread's index, 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 listing's revisions ({"index": n}, {"id": "…"}, or both), "all", or a filter by author
and/or type (insertion, deletion, move, or property: every formatting, paragraph, numbering, table and
section property change). A revision's id is the one the redline's HTML carries, so a change a reviewer picked in
an embedded redline is decided by its anchorId.
[{"type": "reject_revision", "revision": "all"},
{"type": "accept_revision", "revision": {"author": "Jane Doe"}},
{"type": "accept_revision", "revision": {"index": 3}},
{"type": "accept_revision", "revision": {"id": "9f2c4e1a7b3d4c8e9a1f2b3c4d5e6f70"}}]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 3. 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. Revisions no decision covered
keep their id in the edit's redline; their index can change. An empty filter is
refused, and so is a filter that selects no revision, unless the decision sets skip_if_none: true: then it
decides nothing. Accepting every insertion and rejecting every deletion of a document that may lack either,
keeping its moves and formatting changes tracked in the edit's redline:
[{"type": "accept_revision", "revision": {"type": "insertion"}, "skip_if_none": true},
{"type": "reject_revision", "revision": {"type": "deletion"}, "skip_if_none": true}]skip_if_none goes only with a filter. A revision unit is a move (both halves), one author's contiguous insertion or deletion, or one
property change; a replacement is two units. A Word 2003 numbering change (w:numberingChange) cannot be
rejected; rejecting it fails the job.
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}}| Field | Effect in the output |
|---|---|
track_changes | true turns on Track Changes (w:trackRevisions); false turns it off. |
protection | Restrict 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_information | true 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 listing shows the document's own settings.