# Tracked Changes

> Apply reviewers' tracked edits to a contract: read each insertion or deletion with its author, accept or reject it and convert the TX result, in curl or .NET.

- **Product:** ReportingCloud
- **Section:** Documentation
- **Kind:** Topic
- **Path:** ReportingCloud > Document Processing > Tracked Changes
- **2841 words**
- **Web URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/tracked-changes/
- **LLMs URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/tracked-changes/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/reporting-cloud/document-processing/tracked-changes/llms-full.txt

---

Contracts and offers often come back from reviewers with every edit marked: text that was inserted or deleted while change tracking was turned on, each change with its author and time. The review endpoints of the Web API read these tracked changes from a document and accept or reject them one at a time. Your application can apply the decisions a reviewer made in a web page, or a rule such as "accept the changes of the legal department", and keep a document without markup.

### How Tracked Changes Work

A tracked change stays marked in the document until it is accepted, which keeps the change, or rejected, which undoes it: a rejected insertion is removed and a rejected deletion is restored. Two endpoints work with them:

| Endpoint | Request | Response |
|---|---|---|
| [POST /v1/processing/review/trackedchanges](https://docs.textcontrol.com/reporting-cloud/reference/web-api/processing/review-trackedchanges/post/llms-full.txt) | The document as a Base64 string in a JSON string | An array of `TrackedChange` objects |
| [POST /v1/processing/review/removetrackedchange](https://docs.textcontrol.com/reporting-cloud/reference/web-api/processing/review-removetrackedchange/post/llms-full.txt) | The same body, and the query parameters `id` and `accept` | A `ModifiedDocument` object with the changed document and `removed` |

Both endpoints work as follows:

- **Formats.** The document can be RTF, DOC, DOCX or TX, the TX Text Control format. It is sent with each request; the endpoints don't work with the template storage. A document in another format isn't refused: trackedchanges returns an empty array for it, as for a document without tracked changes.
- **One change per request.** removetrackedchange accepts the change with the number `id` when `accept` is `true` and rejects it when `accept` is `false`. It returns the whole document with this one change removed.
- **The result is TX.** The returned document is a Base64 string in the TX format. Send it as it is with the next request, or convert it to DOCX or PDF with `POST /v1/document/convert`, as [Converting Documents](https://docs.textcontrol.com/reporting-cloud/document-processing/converting/llms-full.txt) shows.
- **Quota.** removetrackedchange counts against the document quota, one document per successful request, also when it removes nothing. trackedchanges counts against no quota.

Every request carries an `Authorization` header, as [Authentication](https://docs.textcontrol.com/reporting-cloud/document-processing/authentication/llms-full.txt) describes.

### List the Tracked Changes of a Document

The following commands send *contract.docx* to trackedchanges:

```bash
# Wrap the Base64 string of the document in a JSON string
base64 -w 0 contract.docx | jq -Rs . > contract.json

curl -X POST "https://api.reporting.cloud/v1/processing/review/trackedchanges" \
  -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @contract.json
```

The response has one object per tracked change. The first object of the reference's sample:

```json
[
  {
    "changeKind": 4096,
    "changeTime": "2019-01-01T00:00:00Z",
    "defaultHighlightColor": "60, 255, 0, 0",
    "highlightColor": "60, 87, 174, 168",
    "highlightMode": 3,
    "length": 13,
    "start": 3,
    "id": 0,
    "text": "trackedchange",
    "userName": "ReportingCloudUser"
  }
]
```

A `TrackedChange` object holds:

| Property | Content |
|---|---|
| `id` | The number of the change, which removetrackedchange takes as `id`. The ids start at 0, in the order of the array |
| `changeKind` | `4096` for inserted text, `8192` for deleted text. Format changes are not listed, and moved text is listed as a deletion and an insertion |
| `text` | The inserted or deleted text |
| `start`, `length` | The one-based index of the first character of the change, counted in the main text, the header or the footer that holds it, and the length of the change. A paragraph end counts as one character |
| `userName` | The name of the author, empty if the document names none |
| `changeTime` | The time of the change, in UTC |
| `highlightMode` | The highlight mode of the change: `1` (Never), `2` (Activated) or `3` (Always) |
| `highlightColor`, `defaultHighlightColor` | The highlight color of this change and the default highlight color of tracked changes, as strings of four comma-separated numbers such as `"60, 255, 0, 0"` |

### Accept or Reject a Change

The following command accepts the change with the number 1 and saves the response:

```bash
curl -X POST "https://api.reporting.cloud/v1/processing/review/removetrackedchange?id=1&accept=true" \
  -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @contract.json \
  --output modified.json

# true when the change was removed
jq .removed modified.json

# The document without the change, in the TX format
jq -r .document modified.json | base64 -d > contract.tx
```

The response is a JSON object such as `{"document":"CAcBAA4AAAAAAAAAAAAAAAIAAABsAQAAAQ...==","removed":true}`:

- `document` is the modified document as a Base64 string in the TX format.
- `removed` says whether the change was removed. It is `false`, and `document` is `null`, if the document has no change with the given id. Set `accept=false` to reject the change instead.

To decide on the next change, send the modified document: `jq .document modified.json > contract.json` writes it as the JSON string the next request takes.

> Warning
>
> **Change Ids Shift.** Every request removes one change and returns a new document, in which the ids of the changes that followed the removed one are lower by 1, so read the remaining changes and their numbers from the modified document with trackedchanges before you remove the next one.

### Accept the Changes of One Author With .NET

The NuGet package `TXTextControl.ReportingCloud` has both endpoints in `rc.Processing.Review`. The following console application accepts the changes of one author, rejects all others and saves the result as DOCX:

```csharp
using TXTextControl.ReportingCloud;

var rc = new ReportingCloud(Environment.GetEnvironmentVariable("REPORTINGCLOUD_API_KEY"));

string trustedAuthor = "Alice Johnson";
byte[] document = File.ReadAllBytes("contract.docx");

List<TrackedChange> changes = rc.Processing.Review.GetTrackedChanges(document);

while (changes.Count > 0)
{
    TrackedChange change = changes[0];
    bool accept = change.UserName == trustedAuthor;

    // Accept or reject the change; the result is the document in the TX format
    TrackedChangeModifiedDocument result = rc.Processing.Review.RemoveTrackedChange(document, change.Id, accept);

    if (!result.Removed)
    {
        Console.WriteLine($"Change {change.Id} was not removed.");
        break;
    }

    Console.WriteLine($"{(accept ? "Accepted" : "Rejected")} {change.ChangeKind} by {change.UserName}: {change.Text}");

    // Read the remaining changes from the modified document
    document = result.Document;
    changes = rc.Processing.Review.GetTrackedChanges(document);
}

// Convert the result from the TX format to DOCX
byte[] docx = rc.ConvertDocument(document, ReturnFormat.DOCX);
File.WriteAllBytes("contract-reviewed.docx", docx);
```

The code works as follows:

- `GetTrackedChanges` takes the document as a byte array and returns a list of `TrackedChange` objects with `Id`, `ChangeKind`, `Text`, `Start`, `Length`, `UserName`, `ChangeTime` and the highlight properties. `ChangeKind` is an enumeration with the values `InsertedText` and `DeletedText`.
- `RemoveTrackedChange` takes the document, the number of the change and `true` to accept or `false` to reject. It returns a `TrackedChangeModifiedDocument` whose `Document` holds the modified document as a byte array and whose `Removed` says whether the change was removed. When no change is removed, though, the response's `document` is null and the method throws an `ArgumentNullException`, so a removal that fails ends the loop with an exception, not at the check of `Removed`.
- The loop removes the first change of each list, then lists the changes of the modified document again, so that every number it sends belongs to the document it sends.
- `ConvertDocument` converts the TX result with `POST /v1/document/convert`, a request that counts against the document quota of your plan. Each change itself takes two requests, one to removetrackedchange, which counts against the document quota as well, and one to trackedchanges, which doesn't.

The PHP SDK has the methods `getTrackedChanges($documentFilename)` and `removeTrackedChange($documentFilename, $id, $accept)`. They read the document from a file, and `removeTrackedChange` returns the modified document decoded, under the key `document`: write it to a *.tx* file to pass it on. PHP SDK 4.0.5 can't read a time stamp with fractions of a second, such as `2019-08-06T12:34:13.9188379Z`: `getTrackedChanges` returns such a change with a `change_time` of 0.

### Next Steps

- [Converting Documents](https://docs.textcontrol.com/reporting-cloud/document-processing/converting/llms-full.txt): convert the TX result to DOCX, PDF or another format.
- [Spell Checking](https://docs.textcontrol.com/reporting-cloud/document-processing/spell-checking/llms-full.txt): check the spelling of a text.
- [Managing Templates](https://docs.textcontrol.com/reporting-cloud/document-processing/templates/llms-full.txt): store the reviewed document as a template.
- [Errors and Status Codes](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/llms-full.txt): what a refused request means.

---

## Nearby

- **Previous:** [Spell Checking](https://docs.textcontrol.com/reporting-cloud/document-processing/spell-checking/llms-full.txt)
- **Next:** [Errors and Status Codes](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/llms-full.txt)
