Skip to main content

How to Programmatically Assemble Contracts in C# with PrizmDoc Editor

Let’s say you’re a startup working on a new contract AI solution to automate and standardize the contract creation and review process for your end-users. Your competitive differentiators are your machine learning and natural language processing algorithms that provide an intelligence layer to the contract process, but you still need a document editor to handle document management from within your app.

Accusoft’s PrizmDoc Editor was built with companies like yours in mind. We provide our customers with a comprehensive set of APIs for custom document controls so they can focus on their core competencies. In other words, you provide the app and we provide the editor.

In this blog, we’ll illustrate two fairly basic (but very common) document assembly use cases and provide steps on how to implement our most popular APIs using C# as an example. If you haven’t already read our getting started guide, that’s the place to start. You’ll need to obtain a license key, download our docker image, and set up a proxy route to PrizmDoc Editor server. I’ll wait for you here.


Dynamically Fill Contract Template Fields

For our first use case we will show how a lengthy and error-prone process of manually filling contract template fields can be expedited programmatically using three simple API calls. In this example, we will show how you can upload a contract template in a DOCX format, perform a server-side find and replace process, and then download the newly modified document back to your application.

Step 1: Add the Contract Template to the PrizmDoc Editor Server

First, use the Upload Document API to add the DOCX contract template to the Editor Server. PrizmDoc Editor will convert this file into a proprietary internal format, creating a new document resource with its own unique documentID. In this example, we used this license agreement template called “license-agreement-fields.docx”. Here’s what the code looks like in C#.

using System;
 using System.IO;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;

 namespace Example
 {
   class UploadDocument
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       using (FileStream file = File.OpenRead("license-agreement-fields.docx"))
       {
         var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
         var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";
         var request = new HttpRequestMessage(HttpMethod.Post, $"{API_ROOT}/documents");
         request.Content = new StreamContent(file);
         request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");

         using (var response = await httpClient.SendAsync(request))
         {
           response.EnsureSuccessStatusCode();

           var body = await response.Content.ReadAsStringAsync();

           // Parse the documentId from the returned JSON
           var documentId = JObject.Parse(body)["documentId"];

           Console.WriteLine($"Document uploaded successfully, documentId: {documentId}");
         }
       }
     }
   }
 }

Step 2: Make the Request to Find and Replace Template Fields

Next, use the Find and Replace API to identify specific field placeholders in the contract template that you want to fill, along with the specific information they should contain. In this example, we’re initiating a request to find fields such as company, phone number, and agreement date, and replacing the placeholder values with the appropriate data. Be sure to substitute the {DOCUMENT_ID} placeholder in the code below with the one returned as part of your upload process in step one.

It is important to note that this endpoint is intended to be used by a server-side process that has exclusive access to a document. Do not call this endpoint if a user is actively editing the document or make concurrent calls to this endpoint from the same document. If you do this, changes might not be applied correctly. Instead, use this API to prepare a document before creating a session for user editing or to finalize a document before downloading it into your application.


using System;
 using System.IO;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Text;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;

 namespace Example
 {
   class FindAndReplace
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
       var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";
       var DOCUMENT_ID = "MY_DOCUMENT_ID";

       var request = new HttpRequestMessage(HttpMethod.Post, $"{API_ROOT}/documents/{DOCUMENT_ID}/actions/findAndReplace")
       {
         // This is just a simple example. You can
         // serialize to JSON in whatever way you prefer.
         Content = new StringContent(@"{
           ""rules"": [
             {
               ""find"": {
                 ""text"": ""[COMPANY]""
               },
               ""replaceWith"": {
                 ""text"": ""Accusoft""
               }
             },
             {
               ""find"": {
                 ""text"": ""[PHONE_NUMBER]""
               },
               ""replaceWith"": {
                 ""text"": ""1-813-875-7575""
               }
             },
             {
               ""find"": {
                 ""text"": ""[AGREEMENT_DATE]""
               },
               ""replaceWith"": {
                 ""text"": ""2019-09-12""
               }
             },
             {
               ""find"": {
                 ""text"": ""[CONTACT_EMAIL]""
               },
               ""replaceWith"": {
                 ""text"": ""sales@accusoft.com""
               }
             },
             {
               ""find"": {
                 ""text"": ""[STATE]""
               },
               ""replaceWith"": {
                 ""text"": ""Florida""
               }
             },
             {
               ""find"": {
                 ""text"": ""[COMPANY_ADDRESS]""
               },
               ""replaceWith"": {
                 ""text"": ""4001 N. Riverside Drive Tampa, FL 33603""
               }
             },
             {
               ""find"": {
                 ""text"": ""[PRIVACY_EMAIL]""
               },
               ""replaceWith"": {
                 ""text"": ""privacy@accusoft.com""
               }
             },
             {
               ""find"": {
                 ""text"": ""[SOFTWARE NAME]""
               },
               ""replaceWith"": {
                 ""text"": ""PrizmDoc Editor""
               }
             }
           ]
         }", Encoding.UTF8, "application/json")
       };

       using (var response = await httpClient.SendAsync(request))
       {
         response.EnsureSuccessStatusCode();

         var body = await response.Content.ReadAsStringAsync();
         var totalReplacementsMade = JObject.Parse(body)["totalReplacementsMade"];

         Console.WriteLine($"Document updated. Total replacements made: {totalReplacementsMade}");
       }
     }
   }
 }

Step 3: Download the Newly-Modified Document

At any time, your application can download the current contents of a document as a new DOCX file using the Download Document API. During this process, Editor Server will dynamically create a new DOCX file from the internal document and return this new DOCX file to your application. Note that the document managed by Editor Server continues to exist so additional edits can still be made and you can initiate another request to download the current state as a new DOCX file.


using System;
 using System.IO;
 using System.Net.Http;
 using System.Threading.Tasks;

 namespace Example
 {
   class DownloadDocument
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
       var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";
       var DOCUMENT_ID = "MY_DOCUMENT_ID";

       using (FileStream outputFileStream = File.Open("output-file.docx", FileMode.Create))
       using (var response = await httpClient.GetAsync($"{API_ROOT}/documents/{DOCUMENT_ID}"))
       {
         response.EnsureSuccessStatusCode();

         await response.Content.CopyToAsync(outputFileStream);

         Console.WriteLine("Document has been downloaded.");
       }
     }
   }
 }

Programmatically Insert Common Contract Clauses

For our second use case, we will show how PrizmDoc Editor also makes it possible to programmatically replace content markers with paragraphs or other text — such as specific legal clauses in a list format — to streamline and automate the content creation process. 

Step 1: Upload the Document

Upload your legal document to the PrizmDoc Editor server. In this example, we used this license agreement template called “license-agreement-clauses.docx”. 

 

using System;
 using System.IO;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;

 namespace Example
 {
   class UploadDocument
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       using (FileStream file = File.OpenRead("license-agreement-clauses.docx"))
       {
         var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
         var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";

         var request = new HttpRequestMessage(HttpMethod.Post, $"{API_ROOT}/documents");
         request.Content = new StreamContent(file);
         request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");

         using (var response = await httpClient.SendAsync(request))
         {
           response.EnsureSuccessStatusCode();

           var body = await response.Content.ReadAsStringAsync();

           // Parse the documentId from the returned JSON
           var documentId = JObject.Parse(body)["documentId"];

           Console.WriteLine($"Document uploaded successfully, documentId: {documentId}");
         }
       }
     }
   }
 }

 

Step 2: Make the Find and Replace Request

This step identifies the area in your legal document where you want to add standardized legal clauses. In this example, you’ll see we’ve made each entry part of a numbered list. Be sure to substitute the {DOCUMENT_ID} placeholder in the code below with the one returned as part of your upload process in step one..


using System;
 using System.IO;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Text;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;

 namespace Example
 {
   class FindAndReplace
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
       var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";
       var DOCUMENT_ID = "MY_DOCUMENT_ID";

       var clauses = String.Join("",
         "1. Background. Accusoft Corporation, a Florida corporation, (“Accusoft”) is the owner of all right, title, and interest in the software system known as PrizmDoc Editor (“Program”) and consisting of an installed front-end component plus either an installed back-end component hosted by LICENSEE or back-end services known as Prizm Doc Hosted (“Service”) hosted by Accusoft. LICENSEE desires to receive and use a copy of Program under the terms and conditions stated herein, for a commercial purpose under a Commercial License in Enterprise License Mode (Paragraph 2), Development License Mode (Paragraph 3) or, ISV License Mode (Paragraph 4). LICENSEE may also have entered into a separate license agreement with Accusoft. In that case, the terms of that separate license agreement shall govern only where different from this AGREEMENT.\\n\\n",
         "2. Enterprise Mode License. An Enterprise Mode license may be purchased through sales@accusoft.com. LICENSEE’s permitted use of Program under the Enterprise Mode license is then governed by this Paragraph 2, replacing Paragraphs 3 and 4, and all other terms and Term are according to this Agreement. In that case, Accusoft grants to LICENSEE a limited, non-exclusive, non-assignable, non-transferrable license to install and use Program on one computer for one year beginning on the date the Enterprise Mode license is purchased and as may subsequently be extended by Accusoft on LICENSEE’s request (“Term”).\\n\\n",
         "3. (More clauses)"
       );

       var request = new HttpRequestMessage(HttpMethod.Post, $"{API_ROOT}/documents/{DOCUMENT_ID}/actions/findAndReplace")
       {
         // This is just a simple example. You can
         // serialize to JSON in whatever way you prefer.
         Content = new StringContent(@"{
           ""rules"": [
             {
               ""find"": {
                 ""text"": ""[CLAUSES]""
               },
               ""replaceWith"": {
                 ""text"": """ + clauses + @"""
               }
             }
           ]
         }", Encoding.UTF8, "application/json")
       };

       using (var response = await httpClient.SendAsync(request))
       {
         response.EnsureSuccessStatusCode();

         var body = await response.Content.ReadAsStringAsync();
         var totalReplacementsMade = JObject.Parse(body)["totalReplacementsMade"];

         Console.WriteLine($"Document updated. Total replacements made: {totalReplacementsMade}");
       }
     }
   }
 }

 

Step 3: Share the Document Without Editing Rights

Here, the goal is to allow the end-user to share this document with another staff member to assess — not edit — the contract. This is achieved by creating a session for the end-user to open the contract in view-only mode, making it possible for them to view any changes made without adding or removing contract data.


using System;
 using System.Net.Http;
 using System.Text;
 using System.Threading.Tasks;
 using Newtonsoft.Json.Linq;

 namespace Example
 {
   class CreateSession
   {
     internal static readonly HttpClient httpClient = new HttpClient();

     static void Main(string[] args)
     {
       MainAsync().GetAwaiter().GetResult();
     }

     static async Task MainAsync()
     {
       var PRIZMDOC_EDITOR_ROOT = "http://localhost:21412";
       var API_ROOT = $"{PRIZMDOC_EDITOR_ROOT}/api/v1";
       var DOCUMENT_ID = "MY_DOCUMENT_ID";

       var request = new HttpRequestMessage(HttpMethod.Post, $"{API_ROOT}/sessions")
       {
         // This is just a simple example. You can
         // serialize to JSON in whatever way you prefer.
         Content =  new StringContent(@"{
           ""documentId"": """ + DOCUMENT_ID + @""",
           ""configurationOverrides"": {
             ""editorMode"": ""view"",
             ""allowedEditorModes"": {
               ""view"": true,
               ""suggest"": false,
               ""edit"": false
             },
             ""ribbon"": {
               ""visible"": false
             },
             ""menubar"": {
               ""visible"": false
             },
             ""toolbar"": {
               ""visible"": false
             }
           }
         }", Encoding.UTF8, "application/json")
       };

       using (var response = await httpClient.SendAsync(request))
       {
         response.EnsureSuccessStatusCode();

         var body = await response.Content.ReadAsStringAsync();

         // Parse the sessionId from the returned JSON
         var sessionId = JObject.Parse(body)["sessionId"];

         Console.WriteLine($"Session created. Open the Editor at: {PRIZMDOC_EDITOR_ROOT}/?sessionId={sessionId}");
       }
     }
   }
 }

 


Conclusion

We designed PrizmDoc Editor for businesses that are building their own web applications, but need to extend document handling functionality to include programmatic assembly and docx editing. Now that you know how the assembly side works, you can explore our full editing capabilities in our interactive demo here. We’d love to help  you start exceeding your goals by empowering your development. Contact us today with your questions and we’ll be in touch.

Kim Rubinsak, Sr. Product Marketing Manager, PrizmDoc Suite

Kim joined Accusoft in 2018 as a Senior Product Marketing Manager, providing marketing support for the PrimzDoc Suite of products. Kim received two master degrees in marketing and business administration from the University of Tampa. She has a passion for fostering collaboration across organizations by breaking down departmental silos and learning more about her colleagues as people, how they communicate, and what motivates them. In her free time, Kim enjoys making art, boxing, and snuggling with her dog Khloe.