Introduction to Rich Text Endpoints#

Workiva’s Rich Text endpoints allow customers to interact with content in rich text containers such as section bodies, headers, footers, and footnotes. This guide answers the following questions:

  • How do I retrieve rich text content for a document section?

  • How do I set up new rich text content in a document section?

  • How do I update existing rich text content in a document section?

  • What are some common concepts to consider when working with rich text?

Example Use Cases#

How do I retrieve content from a section?#

The content within a section consists of a rich text container for the body, header, and footer of the Section.

To retrieve the content from a section, first use a GET section request to retrieve all the richTextId content IDs associated with each container.

Once you have these, you can retrieve the actual section content by referencing its specific richTextId. This is done using a GET rich text paragraphs request using the body.richText content ID.

To retrieve the section’s header or footer content, use the headers.odd.richText or footers.odd.richText ID instead of the body.richText ID.

Tip

Headers and footers can be configured to inherit from the previous section, alternate pages, or show different content depending on the page. The odd header or footer is set by default on the first section and additional even, first, or last values can be set based on the section’s configuration. To determine how a section’s header is configured, look at the section’s HeaderProperties or FooterProperties values.

How do I set up new rich text content in a section?#

Section content can be created with a POST rich text edit request using the richTextId and a RichTextBatchEdit. The following example shows what a rich text batch edit would look like for creating the following content in an empty section: rich text example 1

Request#
curl -X POST -H "Authorization: Bearer ${token}" --data @body.json https://api.app.wdesk.com/prototype/platform/content/richText/WA2NiYGJgm7cWr4W6Ka9BHScz56m2AT2FqTmBgekyk399M99I9Bb69BoEt3WHCag/edit
Request Body#
{
    "revision": "2c6438ab454ffa23",
    "isolateEdits": false,
    "data": [
        {
            "type": "insertText",
            "insertText": {
                "insertAt": {
                    "paragraphIndex": 0,
                    "offset": 0
                },
                "text": "Hello world! This is some rich text with a bulleted list followed by a table:\nItem 1\nItem 2\nItem 3\n",
                "format": {
                    "font": "Calibri"
                }
            }
        },
        {
            "type": "formatText",
            "formatText": {
                "selection": {
                    "start": {
                        "paragraphIndex": 0,
                        "offset": 26
                    },
                    "stop": {
                        "paragraphIndex": 0,
                        "offset": 35
                    }
                },

                "bold": true,
                "underline": true,
                "italic": true
            }
        },
        {
            "type": "setListFormat",
            "setListFormat": {
                "selection": {
                    "start": 1,
                    "stop": 3
                },
                "style": "c3R5bGUta2V5OmJ1bGxldC5yb3VuZA"
            }
        },
        {
            "type": "insertTable",
            "insertTable": {
                "insertAt": {
                    "paragraphIndex": 4,
                    "offset": 0
                },
                "columnCount": 5,
                "rowCount": 10
            }
        }
    ]
}

In this example, the batch edit request isolatedEdits is set to false to allow editing content from a previous edit in the same batch edit collection. When inserting new content it often makes sense to disable isolated edits to allow making compounded edits in the same request.

The batch edit collection contains four edits:

  • The first edit at data[0]is an insert text edit that inserts text at the beginning of the section. The line feed character \n is used to denote a new paragraph when inserting text. This allows inserting multiple paragraphs in single insert text edit.

  • The second edit at data[1] is a format text edit that formats part of the text that was inserted in the previous edit. Even though formats can be applied while inserting text, it often is easier to insert all of the text in a single edit and then apply specific formatting instead of creating multiple insert text edits for each format.

  • The third edit at data[2] turns three of the new paragraphs into list items using the setListFormat edit. The style in the edit corresponds to the bullet unordered list style id in the style guide. Style guide style ids can be retrieved using a GET style guide by id request. The selection in this edit uses a paragraph selection, instead of a caret selection, in order to select which paragraphs to format.

  • The fourth edit at data[3] inserts a new table after the last paragraph created in the first insert text edit.

After the request is made, the status of the request can be found through polling the Operations endpoint set in the location header of the batch edit’s response. When the operation is complete a GET request can be made to the completed operation’s resourceUrl to retrieve the rich text batch edit results. The results of this batch edit include the table id associated with the inserted table as well as the new revision:

Request#
curl -X GET -H "Authorization: Bearer ${token}" https://api.app.wdesk.com/prototype/platform/operations/851b155b-fc91-4e78-9f45-f627544844bd/richTextBatchEditResults
Response#
{
    "data": [
        {
            "type": "table",
            "table": "WAz49BOj899Bve7fhEkdIic7H3S7kNC7Dw3Cz099UClUCjlocESss3t501p2tDs99Aw0hzLlke0qGyYFdvK99fGg"
        }
    ],
    "revision": "2c6438ab454fe2ad"
}

How do I update existing rich text content in a section?#

Another common use case for Content endpoints is to update content in an existing section. In this example we’re going to update the example section below to have an additional paragraph between the existing paragraphs, as well as update the text in the last paragraph. Note the initial section content has three paragraphs since the new line between the paragraphs is treated as its own paragraph:
rich text example 2 before

The section text will look like the following after the updates are made:
rich text example 2 after

Request#
curl -X POST -H "Authorization: Bearer ${token}" --data @body.json https://api.app.wdesk.com/prototype/platform/content/richText/WA2NiYGJgm7cWr4W6Ka9BHScz56m2AT2FqTmBgekyk399M99I9Bb69BoEt3WHCag/edit
Request Body#
{
    "revision": "2c6438ab454e8b54",
    "isolateEdits": true,
    "data": [
        {
            "type": "insertText",
            "insertText": {
                "insertAt": {
                    "paragraphIndex": 1,
                    "offset": 0
                },
                "text": "\nCash flow and current debt ratios\n",
                "style": "c3R5bGUta2V5Omgy"
            }
        },
        {
            "type": "deleteText",
            "deleteText": {
                "selection": {
                    "start": {
                        "paragraphIndex": 2,
                        "offset": 111
                    },
                    "stop": {
                        "paragraphIndex": 2,
                        "offset": 133
                    }
                }
            }
        },
        {
            "type": "formatText",
            "formatText": {
                "selection": {
                    "start": {
                        "paragraphIndex": 2,
                        "offset": 135
                    },
                    "stop": {
                        "paragraphIndex": 2,
                        "offset": 168
                    }
                },
                "bold": true
            }
        }
    ]
}

In this batch edit request, isolatedEdits is set to true to make updating content easier. With this setting, all offsets are based on the rich text’s state at the revision included in the request, and do not take into account previous edits in the same batch edit collection.

This batch edit collection contains three edits:

  • The first edit at data[0] inserts a new paragraph after the second paragraph. The insert using the style ID associated with the Heading 2 style in the style guide. Style guide style ids can be retrieved using a GET style guide by id request.

  • The second edit at data[1]deletes the end of the first sentence in the third paragraph. Note that since isolatedEdits is set to true, the paragraph index for the delete text edit does not take into account the two new paragraphs inserted from the first edit.

  • The third edit at data[2]applies a bold format to the beginning of the second sentence in the third paragraph. Since isolatedEdits is set to true, the offset for formatting the text does not take into account the deleted text in the second edit and the paragraph index does not take into account the new paragraphs inserted from the first edit.

If this same edit was attempted with isolatedEdits set to false the following error would be returned since the paragraph at index 1 corresponds to an empty line before the edits were made:

{
    "code": "invalidRequest",
    "details": [
        {
            "code": "invalidRequest",
            "message": "offset is out-of-bounds of the paragraph",
            "target": "/data/1"
        }
    ],
    "message": "invalid batch edits",
    "documentationUrl": "https://developers.workiva.com/prototype-platform/prototype-editrichtextbyid/?rel=error"
}

Common Concepts#

Paragraphs#

Rich text content is organized into a ParagraphsListResult. There is always at least one Paragraph at the start of any rich text content. This starting paragraph may be formatted like any other paragraph; however, the starting paragraph may not be deleted. Based on the properties applied to paragraphs, a paragraph may represent a list item, bullet point, or page break as well as a paragraph separator. Each paragraph object contains a list of elements for the whole paragraph.

Elements#

Elements are the text and content within a Paragraph.

  • The main type of element is a TextSpan. Text spans are similarly formatted unicode characters (for example, italic, or colored green). Each text span will have a length indicating how many unicode characters are within the text span.

Important

The text span length may not match the number of characters since JSON requires UTF-8 encoding not Unicode.

Carets#

Carets represent a location within rich text before or after a character.

A Caret contains two parts: an offset (offset), and a zero based paragraph index, (paragraphIndex). The offset is a sum of the element lengths from the start of a paragraph. Offsets may not be negative.

Rich Text Selections#

Selections represent a range of rich text content containing a set of multiple elements in a paragraph, potentially including the paragraph object.

A RichTextSelection contains two carets: start and stop. The start caret identifies a location in the rich text before the stop caret such that at least one unicode character is between the two carets.

The following examples help demonstrate how rich text selections work in Content endpoints.

Selecting the second character in a paragraph#

rich text selection a1 a2
"selection": {
  "start": { "paragraphIndex": 0, "offset": 1 },
  "stop": { "paragraphIndex": 0, "offset": 2 }
}

Selecting the entire paragraph#

rich text selection a0 a15
"selection": {
  "start": { "paragraphIndex": 0, "offset": 0 },
  "stop": { "paragraphIndex": 0, "offset": 15 }
}

Important

When selecting the whole paragraph, or several whole paragraphs, the paragraph object itself may be included in the selection depending on which action is performed. For example, with this example where all of paragraph 0 is selected, if the selection is used to make the paragraph bold, the text formatting on the paragraph itself will also be set. If the paragraph properties indicate this paragraph is a list item, the glyph for the list item will become bold too. Look at the description of the edit being performed with the selection to see if it will include the paragraph object for whole paragraph selections.

Selecting across paragraphs#

When selecting across paragraphs, the paragraph object within the selection will be affected by the edit too. For the following example paragraph 0 will be modified as well. If the edit using this selection is to delete the content, then paragraph 1 will also be removed, and the text “A mote of dust” would move to the first paragraph.

rich text selection a0 b0
"selection": {
  "start": { "paragraphIndex": 0, "offset": 0 },
  "stop": { "paragraphIndex": 1, "offset": 0 }
}

Selecting individual paragraphs#

Each paragraph (with the exception of the initial paragraph) can be selected by itself to be deleted or formatted as needed. The following shows how to select the second paragraph. If this selection was used in a delete text edit, the text “A mole of dust” would move to the first paragraph.

rich text selection a15 b0
"selection": {
  "start": { "paragraphIndex": 0, "offset": 15 },
  "stop": { "paragraphIndex": 1, "offset": 0 }
}

Selecting embedded content#

Selecting across elements other than text spans is the same as selecting across text spans. The following example shows how an embedded table could be selected for a rich text edit to delete the table.

rich text selection c2 c3
"selection": {
  "start": { "paragraphIndex": 2, "offset": 2 },
  "stop": { "paragraphIndex": 2, "offset": 3 }
}

Paragraph Selections#

Paragraph selections represent a range of rich text paragraph objects. This selection is typically used for changing indents, lists, alignment, and other paragraph properties and formats.

A ParagraphSelection contains a start and stop paragraph index to indicate the paragraphs to include in the selection. The start and stop paragraph indexes are inclusive.

The following example helps demonstrates selecting the first two paragraphs:

paragraph selection a b
"selection": { 
  "start": 0, 
  "stop": 1 
}