Action Link Endpoints Overview

An Action Link Endpoint is an endpoint on a web application to which Advance sends data when a user activates an action link. An endpoint is required for action links to function.

Overview

An action link appears to a user as a button, either on the Work Item Details pane or in interview. When a user clicks the button, Advance sends a request to the URL you specify when creating the action link. This is the endpoint URL, the address of an endpoint on your own web application.

Your endpoint must be able to receive data sent from Advance and return an HTML page. Advance displays the returned HTML page in a new window for the user that activated the action link. The HTML page may contain whatever user interface or scripting elements you want. In this way, a user can access external third-party workflows from within Advance, using your web application as an intermediary.

This topic explains:

  • The data sent from Advance to your application.
  • The HTML page that must be returned from your application to Advance.
  • How to configure the returned HTML page to communicate with Advance.

Prerequisites

You should be familiar with the JavaScript window.postMessage method. You use this to communicate between the web browser window displaying your HTML page and the window displaying Advance. 

Workflow

You must create the action link endpoint in your own web application before creating a new action link in Advance. When you create a new action link, you must know the URL to the endpoint in your web application to which Advance will send data. When a user activates an action link, Advance displays the HTML page returned from your endpoint to the user.

Data sent from Advance to your web application endpoint

Advance sends data to your endpoint that enables you to identify the work item from where the user clicked the action link.

Request Type

Advance sends a POST request to your endpoint.

Request Data

The payload of the request body from Advance contains a token. The token includes the work item on which the user activated the action link, the work group to which the work item belongs and the status of the work item. This token will supersede the table of form data below.

Key Description Example
token An ID including the work item, work group and status of the work item. The status can have the following values:
  • New — work item created; its interview has not been opened by a user
  • InProgress — work item interview in progress
  • Complete — work item interview completed; the work item may have assembled documents
eyJXb3JrSXRlbUlkIjoiODUwZDhhODQtNzU1Yy00NjI0LWJmMzYtMjQxOGN
kZWViMjI0IiwiV29ya0dyb3VwSWQiOiIzY2JlZTU3Ny00YWIzLTQ4NGMtYTk1
NC1mNzFkODI5OTJlMTEiLCJTdGF0dXMiOiJOZXciLCJTY29wZSI6IkFjdGlvb
kxpbmsvQWN0aW9uIiwiVGVuYW5jeU1vbmlrZXIiOiJtYW51YWwxIiwiU2Vzc2l
vbkV4cGlyZXMiOiIxNjQ3OTAyOTA3IiwiaHR0cDovL3NjaGVtYXMueG1sc29hc
C5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIjoiQW
RtaW5pc3RyYXRvciIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwM
DUvMDUvaWRlbnRpdHkvY2xhaW1zL3N1cm5hbWUiOiJVc2VyIiwiaHR0cDovL3Nj
aGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1
haWxhZGRyZXNzIjoiYWRtaW5pc3RyYXRvckBleGFtcGxlLmNvbSIsImh0dHA6
Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xh
aW1zL25hbWVpZGVudGlmaWVyIjoiZTliYzM1OGYtOTQ1Yy00YmYwLTg0MzEtO
WU5ZmU1MjYxODEzIiwiaWF0IjoxNjQ3ODc0MTQ3LCJqdGkiOiJlNTNjNTkwMj
gxZDQ0YTg1ODg3MzgzYzliYzY4MzJkYiIsImV4cCI6MTY0Nzg3NDMyNyw iYX
VkIjoiMzMzZTE3ZWItODRkZi00OGVmLWI4NGMtNmYyNGVkMjc1YjQ2In0.718c
aUwmO4wBAN9nYF28TXoViejFixjp-MAHaQAR9uBRVCeazY71dE6w8835jeEN

Note the following form data will be deprecated in a future release. The token above will supersede these values.

Key Description Example
workItemId The ID of the work item on which the user activated the action link. 787fea21-7fff-4be4-a693-65919a4c82b8
workGroupId The ID of the work group to which the work item belongs 787fea21-7fff-4be4-a693-65919a4c82b8
workItemStatus The status of the work item. The status can have the following values:
  • New — work item created; its interview has not been opened by a user
  • InProgress — work item interview in progress
  • Complete — work item interview completed; the work item may have assembled documents
InProgress

See the code example below for an example of reading this form data.

HMAC authentication

See Using HMAC Authentication for information about using HMAC to verify the integrity of data sent from Advance to your own web application.

Unlike webhooks, where using an HMAC is optional, action links must use HMAC authentication.

HTML page returned from your application to Advance

Your endpoint must return an HTML page to Advance. Advance opens this page in a new window for the user. This window communicates with Advance using the listeners and commands described in the following section. This HTML page contains your user interface that enables the user to interact with a third-party workflow.

See the code example below for an example HTML page.

HTML page communication with Advance

You can use the JavaScript window.postMessage method to enable communication between the web browser window displaying your HTML page and the web browser window displaying Advance. For your page window to communicate with the Advance window, you require the following:

  • A session ID — appears in the URL query string; used to identify the action link communication session with Advance.
  • An event listener — used to listen for events from Advance.
  • A send function — used to send commands to Advance.

See the code example below for an example HTML page.

Session ID

When Advance makes a request to your endpoint, it adds an hdaSessionId query string parameter to the URL. Advance uses this session ID to identify the action link in use. For example:

https://yourapp.yourdomain.com/api/handleactionlink?hdaSessionId=V6e3p0f337CJVim1tzEx

You must use this session ID when sending commands to Advance from your page.

Event listener

Advance may need to send messages to your HTML page. For example, any error messages that occur. Your page should listen for message events on its own window when your page has loaded. 

Send function

The Send function is a JavaScript function that appears in your HTML page. The purpose of this function is to send commands from the window displaying your HTML page to the window displaying Advance, using the JavaScript window.postMessage method. Advance only accepts the commands listed in the table below.  

Commands

The HTML page you return should use JavaScript to send commands to Advance. The available commands are:

Command name Parameters Description
hotdocsadvance/connect None Used to initiate the action link session with Advance. Your page should ideally send a connect message when it first loads, to acknowledge it is ready to begin communicating with Advance.
hotdocsadvance/close None Tell Advance to close the window.
hotdocsadvance/error message: string Advance sends this command to your page if an exception occurs while executing a registered command handler. The message is taken from the exception.

You must include the hotdocsadvance/ prefix when using commands. This prefix identifies postMessage events intended specifically for Advance. Advance ignores any commands that do not use this prefix.

Command Message

The message parameter used by postMessage must contain the following data:

Parameter name Type Description Example data
sessionId string The session ID, taken from the hdaSessionId query string parameter. V6e3p0f337CJVim1tzEx
name string The name of the command sent to Advance. hotdocsadvance/close
data any Any data to be sent with the command.  

For example, when using the close command:

{
  sessionId: "V6e3p0f337CJVim1tzEx",
  name: "hotdocsadvance/close",
  data: null
}

Data Sent from your Endpoint to Advance

The response sent back to Advance from your endpoint must be a JSON string that conforms to the HotDocs answer JSON format. For example, this answer set returns values for text, number, date, true/false, multi select, and repeat variables:

"[
    {""n"":""textVar1"",""t"":""TX"",""v"":""Francis Smith""},
    {""n"":""numberVar1"",""t"":""NU"",""v"":123},
    {""n"":""dateVar1"",""t"":""DA"",""v"":""31 12 1999""},
    {""n"":""trueFalseVar1"",""t"":""TF"",""v"":true},
    {""n"":""multiSelectVar1"",""t"":""TL"",""v"":{""v"":[""Option A"", ""Option B""]}},
    {""n"":""repeatVar1"",""t"":""TX"",""v"":[""Employed Full-Time"", ""Employed Part-Time"", ""Unemployed""]}

]"

Getting Answer Data from an Existing Interview

You can use the HotDocs JavaScript API HD$.GetJSONAnswers function with an open HotDocs interview to quickly generate an example answer set. Running the command in the console of your browser's Developer Tools window retrieves the JSON answer data from the in-progress interview.

Validating Your Answer Data

You should validate your answer data before sending it to Advance. See Validating HotDocs Answer Sets for more information.

Code Example (C# and ASP.NET)

This example assumes that you are using a Microsoft .Net ASP.NET Core Web App (MVC) project named ActionLinkExample for your web application. 

Web Application Endpoint

HomeController.cs

using System;

using System.IdentityModel.Tokens.Jwt;

using System.Linq;

using System.Text;

using Microsoft.AspNetCore.Mvc;

using Microsoft.IdentityModel.Tokens;

namespace ApprovalWorkflow.Controllers

{

public class HomeController : Controller

    {

        [HttpPost]

        public IActionResult Index()

        {

            // Configure with your HMAC Secret and Action Link ID

            const string hmacSecret = "^/g.B-Cv2Dm_o5bY{YR,FJ1KG{:5gOjy|&&!A[17ieQ8J^q9O9il%-/X4Yx~3Ql";

            const string actionLinkId = "09517859-9eab-4960-8bc7-24af9dfb521a";

            // Read the JWT from the request body

            using var stream = new StreamReader(Request.Body);
            var token = await stream.ReadToEndAsync();

            try

            {

                // Validate the token

                new JwtSecurityTokenHandler().ValidateToken(token, new TokenValidationParameters

                {

                    ValidateIssuerSigningKey = true,

                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(hmacSecret)),

                    ValidateAudience = true,

                    ValidAudience = actionLinkId,

                    ValidateIssuer = false,

                    ClockSkew = TimeSpan.Zero

                }, out var validatedToken);

                // Get the claims

                var claims = (validatedToken as JwtSecurityToken)?.Claims.ToList();

                if (claims?.Any() != true)

                {

                    // There were no valid claims

                    return Unauthorized();

                }

                // Get the context from the claims

                var workItemId = claims.Find(c => c.Type == "WorkItemId");

                var workGroupId = claims.Find(c => c.Type == "WorkGroupId");

                var workItemStatus = claims.Find(c => c.Type == "Status");

                // Your other code will go here; for example, retrieving more data from Advance (e.g.

                // using the workItemId to retrieve assembled documents), retrieving data from another

                // service, and so on.

                // Return the HTML page to be displayed by Advance

                return View();

            }

            catch

            {

                // The token was not valid

                return Unauthorized();

            }

        }

    }

}

View Returned by Endpoint

Index.cshtml

<!doctype html>
<html lang="en">
    <head>
        <title>Action Link Test Page</title>
    </head>
    <body>
        <h1>Action Link Test Page</h1>
        <button onclick="send('close')">Close Window</button>
        <div id="context"></div>
        <script>
            // Listen for message events from Advance
            window.addEventListener('message', function (event) {
                if (event.data !== undefined && event.data.name !== undefined) {
                    switch (event.data.name) {
                        case "hotdocsadvance/error":
                            alert(`An error occurred: ${event.data.message}`);
                            break;
                    }
                }
            });
            
            // Get the Advance session ID from the page URL
            const sessionId = new URL(location.href).searchParams.get("hdaSessionId");
            
            const targetWindow = window.opener || window.parent;
            
            // targetOrigin is set to '*' for testing purposes only; you must always specify the
            // URI for the expected origin of targetWindow for security reasons.
            const targetOrigin = "*";
            
            // Function for sending commands from this window to the Advance window, using postMessage
            function send(command, data) {
                if (targetWindow !== window.self) {
                    const message = { sessionId, name: `hotdocsadvance/${command}`, data };
                    targetWindow.postMessage(message, targetOrigin);
                }
            }
            // Send an initial 'connect' message to Advance             
            send("connect");
        </script>
    </body>
</html>

Testing your endpoint

Once you have created an appropriate endpoint in your web application, you can test it with Advance by:

  1. Creating a new action link in Advance, using the URL of your new endpoint; make a note of the work group to which you assign the new action link.
  2. Open the work group to which you assigned the action link.
  3. Either open an existing work item or create a new work item.
  4. Notice a button labelled with the name of your new action link either:
    • On the work item details pane for the selected work item, or
    • In the work item's interview.
  5. Click the button; the window for your action link opens.