Integrate the Document Viewer
The Document Viewer is a web component that lets users edit templates and prepare documents for signing — directly inside your application. This guide shows how to connect it to the GraphQL API to build a complete signing workflow.
Overview
A typical integration follows these steps:
- Authenticate — get a token for the viewer
- Upload a template — via the GraphQL API
- Render the viewer — pass the template ID and token
- Capture events — the viewer emits
updateandvalidateevents as the user works - Send the document — call the
sendmutation with the recipient and role data from the viewer
Authentication
The viewer needs a valid access token passed to its token attribute. Get one using the authentication flow:
const LEGALESIGN_AUTH_ENDPOINT = 'https://cognito-idp.eu-west-2.amazonaws.com';
const GRAPHQL_ENDPOINT = 'https://graphql.uk.legalesign.com/graphql';
const getToken = async () => {
const response = await fetch(LEGALESIGN_AUTH_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Target': 'AWSCognitoIdentityProviderService.InitiateAuth',
},
body: JSON.stringify({
AuthFlow: 'USER_PASSWORD_AUTH',
ClientId: '<your-client-id>',
AuthParameters: {
USERNAME: '<your-username>',
PASSWORD: '<your-password>',
},
}),
});
const data = await response.json();
return data.AuthenticationResult.AccessToken;
};
Compose Mode — Send a Pre-built Document
Compose mode is for workflows where your system already knows the recipients. The user just places signature fields and sends.
1. Render the viewer
<ls-document-viewer
id="viewer"
templateid="dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
token="YOUR_ACCESS_TOKEN"
mode="compose"
recipients='[
{"email": "jane@example.com", "firstname": "Jane", "lastname": "Smith", "signerIndex": 1},
{"email": "john@example.com", "firstname": "John", "lastname": "Doe", "signerIndex": 2}
]'
filtertoolbox="signature|initials|date|text"
>
<span slot="right-button">
<button id="send-btn" disabled>Send</button>
</span>
</ls-document-viewer>
2. Track validation and role IDs
As the user adds fields, the viewer emits events. You need two things from these events:
validate— tells you whether the template has all required fields (at minimum, one signature per recipient)update— provides the current template state, includingroleIdfor each participant
const viewer = document.getElementById('viewer');
const sendBtn = document.getElementById('send-btn');
let isValid = false;
const recipientRoles = {};
viewer.addEventListener('validate', (e) => {
isValid = e.detail.valid;
sendBtn.disabled = !isValid;
});
viewer.addEventListener('update', (e) => {
const roles = e.detail.template.roles;
roles.forEach((role) => {
recipientRoles[role.signerIndex] = role.id;
});
});
The roleId from the update event maps each recipient to their fields on the template. You must include it when sending.
3. Send the document
When the user clicks send, call the send mutation with the recipient data and role IDs captured from the viewer:
sendBtn.addEventListener('click', async () => {
const token = await getToken();
const recipients = [
{
firstName: 'Jane',
lastName: 'Smith',
email: 'jane@example.com',
role: 'Signer',
roleId: recipientRoles[1],
signerIndex: 1,
order: 1,
},
{
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
role: 'Signer',
roleId: recipientRoles[2],
signerIndex: 2,
order: 2,
},
];
const response = await fetch(GRAPHQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query: `mutation SendDocument {
send(input: {
groupId: "YOUR_GROUP_ID"
templateId: "dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
title: "Contract - Jane Smith & John Doe"
recipients: ${JSON.stringify(recipients).replace(/"(\w+)":/g, '$1:')}
sequentialSigning: true
})
}`,
}),
});
const result = await response.json();
const taskId = result.data.send;
// Poll for completion
console.log('Task started:', taskId);
});
The send mutation returns a task ID. Poll the task query to check when sending is complete.
Editor Mode — Template Creation
Editor mode gives users the full template editing experience — adding roles, placing fields, configuring options. Use this when you want users to build reusable templates.
<ls-document-viewer
id="editor"
templateid="dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
token="YOUR_ACCESS_TOKEN"
mode="editor"
></ls-document-viewer>
In editor mode, the update event fires whenever the template changes. You can use this to track the template state or show a save indicator:
const editor = document.getElementById('editor');
editor.addEventListener('update', (e) => {
console.log('Template updated:', e.detail);
});
editor.addEventListener('validate', (e) => {
console.log('Template valid:', e.detail.valid);
});
Template changes are saved automatically by the viewer — you don't need to call any GraphQL mutation to persist edits.
React Integration
The React wrapper provides the same functionality with React-style event handling:
import { useState, useEffect } from 'react';
import { LsDocumentViewer } from 'legalesign-document-viewer-react';
const DocumentCompose = ({ templateId, recipients }) => {
const [token, setToken] = useState(null);
const [isValid, setIsValid] = useState(false);
const [roleMap, setRoleMap] = useState({});
useEffect(() => {
getToken().then(setToken);
}, []);
const handleValidate = (e) => {
setIsValid(e.detail.valid);
};
const handleUpdate = (e) => {
const roles = e.detail.template.roles;
const map = {};
roles.forEach((role) => {
map[role.signerIndex] = role.id;
});
setRoleMap(map);
};
const handleSend = async () => {
const mappedRecipients = recipients.map((r, i) => ({
...r,
roleId: roleMap[r.signerIndex],
order: i + 1,
}));
const response = await fetch(GRAPHQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query: `mutation SendDocument {
send(input: {
groupId: "YOUR_GROUP_ID"
templateId: "${templateId}"
title: "Document"
recipients: ${JSON.stringify(mappedRecipients).replace(/"(\w+)":/g, '$1:')}
})
}`,
}),
});
const result = await response.json();
console.log('Task ID:', result.data.send);
};
if (!token) return null;
return (
<LsDocumentViewer
templateid={templateId}
token={token}
mode="compose"
recipients={JSON.stringify(recipients)}
onValidate={handleValidate}
onUpdate={handleUpdate}
>
<div slot="right-button">
<button onClick={handleSend} disabled={!isValid}>
Send
</button>
</div>
</LsDocumentViewer>
);
};
Witnesses
To add a witness for a signer, set roleType: "WITNESS" and use a signerIndex of the parent signer's index + 100. For example, a witness for signer 2 has signerIndex: 102:
<ls-document-viewer
mode="compose"
recipients='[
{"email": "signer@example.com", "firstname": "Alice", "lastname": "Jones", "signerIndex": 1},
{"email": "signer2@example.com", "firstname": "Bob", "lastname": "Brown", "signerIndex": 2},
{"email": "witness@example.com", "firstname": "Carol", "lastname": "White", "signerIndex": 102, "roleType": "WITNESS"}
]'
...
></ls-document-viewer>
Key Concepts
| Viewer concept | GraphQL type | Description |
|---|---|---|
templateid attribute | Template | The template being edited or composed |
recipients attribute | RecipientInput | Recipients passed to compose mode |
roleId from update event | Role | Links a recipient to their fields on the template |
validate event | — | Whether the template has all required fields |
send mutation | send | Sends the document after composition |
token attribute | — | Access token from authentication |
mode="compose" | — | Streamlined mode for placing fields and sending |
mode="editor" | — | Full template editing mode |
experience in send input | Experience | Controls branding, emails, and signing page — see Experiences explained |
Related
- Document Viewer reference — full attribute and event reference
- send mutation — send a document
- DocumentSendSettingsInput — full send input structure
- Sending methods explained — send vs sendBatch vs sendBulk
- Upload a PDF template — upload templates via the API
- Authentication — get an access token