Upload a PDF Template
This guide walks you through the complete process of creating a template and uploading a PDF file to use as your document template in Legalesign.
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 needed for upload
- Upload your PDF 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 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 gives you a template ID that you'll use 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) {
template {
id
title
groupId
created
}
}
}
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
The mutation returns a template object. Save the id field — this is what you'll use to upload your PDF.
Example response:
{
"data": {
"createTemplate": {
"template": {
"title": "Employment Contract Template",
"groupId": "grpYourGroupId"
}
}
}
}
The template ID is a Base64-encoded string. Save this ID - you'll need it for the next step.
Step 3: Request Upload Permission
Now that you have a template ID, request a pre-signed URL to upload your PDF.
query GetUploadUrl {
upload(
id: "dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx",
uploadType: TEMPLATE,
extension: "pdf"
) {
url
}
}
Parameters
- id: The template ID from Step 2
- uploadType: Must be
TEMPLATEfor PDF templates - extension: Must be
"pdf"for template files
The response will contain a pre-signed URL:
{
"data": {
"upload": {
"url": "https://s3.amazonaws.com/bucket/path?signature=..."
}
}
}
This URL is only valid for 15 minutes. If it expires, simply request a new one.
Step 4: Upload Your PDF
Use the pre-signed URL 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 LEGALESIGN_AUTH_ENDPOINT = 'https://cognito-idp.eu-west-2.amazonaws.com';
const CLIENT_ID = '<your-client-id>';
const USERNAME = '<your-username>';
const PASSWORD = '<your-password>';
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: CLIENT_ID,
AuthParameters: { USERNAME, PASSWORD }
})
});
const data = await response.json();
if (data.__type) throw new Error(`Auth failed: ${data.message}`);
return data.AuthenticationResult.AccessToken;
};
const uploadPdfTemplate = async (groupId, title, pdfFilePath) => {
const graphqlEndpoint = 'https://graphql.uk.legalesign.com/graphql';
console.log('Authenticating...');
const authToken = await getToken();
// Step 1: Create the template
console.log('Creating template...');
const createResponse = await fetch(graphqlEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
body: JSON.stringify({
query: `
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
template {
id
title
}
}
}
`,
variables: {
input: {
groupId: groupId,
title: title
}
}
})
});
const createResult = await createResponse.json();
const templateId = createResult.data.createTemplate.template.id;
console.log('Template created with ID:', templateId);
// Step 2: Get upload URL
console.log('Requesting upload URL...');
const uploadUrlResponse = await fetch(graphqlEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
body: JSON.stringify({
query: `
query GetUploadUrl($id: ID!) {
upload(
id: $id,
uploadType: TEMPLATE,
extension: "pdf"
) {
url
}
}
`,
variables: {
id: templateId
}
})
});
const uploadUrlResult = await uploadUrlResponse.json();
const uploadUrl = uploadUrlResult.data.upload.url;
console.log('Upload URL received');
// Step 3: 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) {
template {
id
title
}
}
}
''')
create_result = client.execute(
create_mutation,
variable_values={
'input': {
'groupId': group_id,
'title': title
}
}
)
template_id = create_result['createTemplate']['template']['id']
print(f'Template created with ID: {template_id}')
# Step 2: Get upload URL
print('Requesting upload URL...')
upload_query = gql(f'''
query {{
upload(
id: "{template_id}",
uploadType: TEMPLATE,
extension: "pdf"
) {{
url
}}
}}
''')
upload_result = client.execute(upload_query)
upload_url = upload_result['upload']['url']
print('Upload URL received')
# Step 3: 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) {
template {
id
title
}
}
}
",
Variables = new
{
input = new
{
groupId = groupId,
title = title
}
}
};
var createResponse = await graphQLClient.SendMutationAsync<dynamic>(createMutation);
string templateId = createResponse.Data.createTemplate.template.id;
Console.WriteLine($"Template created with ID: {templateId}");
// Step 2: Get upload URL
Console.WriteLine("Requesting upload URL...");
var uploadQuery = new GraphQLRequest
{
Query = $@"
query {{
upload(
id: ""{templateId}"",
uploadType: TEMPLATE,
extension: ""pdf""
) {{
url
}}
}}
"
};
var uploadResponse = await graphQLClient.SendQueryAsync<dynamic>(uploadQuery);
string uploadUrl = uploadResponse.Data.upload.url;
Console.WriteLine("Upload URL received");
// Step 3: 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 the PDF - Checks it's a valid PDF format
- 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!
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
Request a new upload URL by running the upload query again. URLs expire after 15 minutes.
"Invalid PDF" Error
Open the PDF in a PDF reader to verify it's valid, then re-export or re-save it.