Upload a File as a Template
This guide walks you through the complete process of creating a template and uploading a PDF, image, or Word file to use as your document template in Legalesign.
Need real-time feedback on upload processing? See Track Upload Progress with Subscriptions.
What You'll Learn
By the end of this guide, you'll know how to:
- Create a new template in your Legalesign group
- Get the template ID and upload URL from the mutation response
- Upload your source file to the template
- Verify the upload was successful
Prerequisites
Before you start, make sure you have:
- A Legalesign account with API access
- Your authentication credentials (see our authentication guide)
- A PDF, image, or Word file ready to upload (maximum 50MB)
- Your group ID (the workspace where you want to create the template)
The Complete Process
Step 1: Create a Template
First, create an empty template in Legalesign. This returns both the template ID and a pre-signed upload URL for the PDF upload. To do this you'll need to run a GraphQL mutation, if you haven't done this before see the Introduction to GraphQL.
What is a Template?
A template is a reusable document structure in Legalesign. Once you upload a PDF to a template, you can:
- Add signature fields and form fields
- Send it to multiple recipients
- Reuse it for different signers
GraphQL Mutation
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
id
uploadUrl
}
}
Input Variables
{
"input": {
"groupId": "grpYourGroupAPIId",
"title": "Employment Contract Template"
}
}
Parameters Explained
- groupId: The base 64 ID of your group/workspace (you can grab this from the URL in Console https://console.legalesign.com/)
- title: A descriptive name for your template (you can change this later)
Step 2: Extract the Template ID and Upload URL
The mutation returns a templateCreateOutput object. Save the id field and the uploadUrl string.
Example response:
{
"data": {
"createTemplate": {
"id": "dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx",
"uploadUrl": "https://s3.amazonaws.com/bucket/path?signature=..."
}
}
}
The template ID is a Base64-encoded string. Save both values from the response. The uploadUrl is short-lived and should be used promptly.
Step 3: Upload Your PDF
Use the returned uploadUrl to upload your PDF file directly to S3. This will be different
depending on your development stack. In our javascript example we've used fetch but
you can use other libraries including aws-amplify.
Complete Working Examples
- JavaScript
- Python
- C#
import fs from 'fs';
const AUTH_TOKEN = '<token-from-authentication-guide>';
const uploadPdfTemplate = async (groupId, title, pdfFilePath) => {
const graphqlEndpoint = 'https://graphql.uk.legalesign.com/graphql';
// Step 1: Create the template
console.log('Creating template...');
const createResponse = await fetch(graphqlEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${AUTH_TOKEN}`
},
body: JSON.stringify({
query: `
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
id
uploadUrl
}
}
`,
variables: {
input: {
groupId: groupId,
title: title
}
}
})
});
const createResult = await createResponse.json();
const templateId = createResult.data.createTemplate.id;
const uploadUrl = createResult.data.createTemplate.uploadUrl;
console.log('Template created with ID:', templateId);
// Step 2: Upload the PDF
console.log('Uploading PDF...');
const fileData = fs.readFileSync(pdfFilePath);
const uploadResponse = await fetch(uploadUrl, {
method: 'PUT',
body: fileData,
headers: {
'Content-Type': 'application/pdf'
}
});
if (!uploadResponse.ok) {
throw new Error(`Upload failed: ${uploadResponse.statusText}`);
}
console.log('PDF uploaded successfully!');
return {
success: true,
templateId: templateId,
title: title
};
};
// Usage example
uploadPdfTemplate(
'grpYourGroupId',
'Employment Contract',
'./contract.pdf'
).then(result => {
console.log('Complete!', result);
}).catch(error => {
console.error('Error:', error);
});
No additional dependencies needed — Node.js 18+ includes fetch natively.
import requests
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
def upload_pdf_template(graphql_endpoint, auth_token, group_id, title, pdf_file_path):
transport = RequestsHTTPTransport(
url=graphql_endpoint,
headers={'Authorization': auth_token}
)
client = Client(transport=transport, fetch_schema_from_transport=True)
# Step 1: Create the template
print('Creating template...')
create_mutation = gql('''
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
id
uploadUrl
}
}
''')
create_result = client.execute(
create_mutation,
variable_values={
'input': {
'groupId': group_id,
'title': title
}
}
)
template_id = create_result['createTemplate']['id']
upload_url = create_result['createTemplate']['uploadUrl']
print(f'Template created with ID: {template_id}')
# Step 2: Upload the PDF
print('Uploading PDF...')
with open(pdf_file_path, 'rb') as f:
file_data = f.read()
response = requests.put(
upload_url,
data=file_data,
headers={'Content-Type': 'application/pdf'}
)
if response.status_code != 200:
raise Exception(f'Upload failed: {response.status_code}')
print('PDF uploaded successfully!')
return {
'success': True,
'templateId': template_id,
'title': title
}
if __name__ == '__main__':
result = upload_pdf_template(
'https://graphql.uk.legalesign.com/graphql',
'Bearer YOUR_TOKEN',
'grpYourGroupId',
'Employment Contract',
'./contract.pdf'
)
print('Complete!', result)
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;
using Newtonsoft.Json.Linq;
public class PdfTemplateUploader
{
private readonly GraphQLHttpClient graphQLClient;
public PdfTemplateUploader(string graphqlEndpoint, string authToken)
{
graphQLClient = new GraphQLHttpClient(graphqlEndpoint, new NewtonsoftJsonSerializer());
graphQLClient.HttpClient.DefaultRequestHeaders.Add("Authorization", authToken);
}
public async Task<UploadResult> UploadPdfTemplate(
string groupId,
string title,
string pdfFilePath)
{
// Step 1: Create the template
Console.WriteLine("Creating template...");
var createMutation = new GraphQLRequest
{
Query = @"
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
id
uploadUrl
}
}
",
Variables = new
{
input = new
{
groupId = groupId,
title = title
}
}
};
var createResponse = await graphQLClient.SendMutationAsync<dynamic>(createMutation);
string templateId = createResponse.Data.createTemplate.id;
string uploadUrl = createResponse.Data.createTemplate.uploadUrl;
Console.WriteLine($"Template created with ID: {templateId}");
// Step 2: Upload the PDF
Console.WriteLine("Uploading PDF...");
using var httpClient = new HttpClient();
var fileBytes = await File.ReadAllBytesAsync(pdfFilePath);
var content = new ByteArrayContent(fileBytes);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
var putResponse = await httpClient.PutAsync(uploadUrl, content);
if (!putResponse.IsSuccessStatusCode)
{
throw new Exception($"Upload failed: {putResponse.StatusCode}");
}
Console.WriteLine("PDF uploaded successfully!");
return new UploadResult
{
Success = true,
TemplateId = templateId,
Title = title
};
}
}
public class UploadResult
{
public bool Success { get; set; }
public string TemplateId { get; set; }
public string Title { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var uploader = new PdfTemplateUploader(
"https://graphql.uk.legalesign.com/graphql",
"Bearer YOUR_TOKEN"
);
var result = await uploader.UploadPdfTemplate(
"grpYourGroupId",
"Employment Contract",
"./contract.pdf"
);
Console.WriteLine($"Complete! Template ID: {result.TemplateId}");
}
}
What Happens After Upload?
Once your PDF is uploaded, Legalesign automatically:
- Scans for viruses - Ensures the file is safe
- Validates or converts the file - Checks PDFs are valid, or converts supported files such as Word documents and images into PDF
- Extracts page information - Gets page count and dimensions
- Processes the file - Optimizes it for viewing and signing
- Stores it securely - Moves it to permanent storage
This process usually takes a few seconds. Once complete, your template is ready to use!
Adding Signatures and Fields
If you want to automate participants and field placement, you can prepare the source file before upload in a couple of different ways:
- Text tags - Add Legalesign text tags into the source document so participants, signature fields, and form fields can be created automatically during processing. See the REST API explanation in the quickstart tutorial and the Convert text tags endpoint reference.
- Embedded PDF fields - If your PDF already contains embedded form fields, Legalesign can use those as part of the upload and template preparation workflow.
Next Steps
Now that you have a template with a PDF, you can:
- Add signature fields - Use the
createTemplateElementmutation to add fields - Create roles - Define who will sign the document
- Send for signing - Use the
sendmutation to send to recipients
Common Issues and Solutions
"No permission" Error
Verify your group ID is correct and you're authenticated with the right account.
"File too large" Error
Compress your PDF — the maximum is 50MB.
Upload URL Expired
Use the uploadUrl returned by createTemplate promptly. If it expires before you upload, request a fresh URL with the upload query using the saved template ID.
"Invalid PDF" Error
Open the PDF in a PDF reader to verify it's valid, then re-export or re-save it.
Track Upload Progress
To get real-time feedback on upload processing (scanning, validation, completion), use subscriptions. See Track Upload Progress with Subscriptions.