How to Sign PDFs Using Digital Certificates in Node.js/JavaScript/TypeScript
Introduction
In this guide, we will walk you through the process of signing PDFs digitally using Node.js environment. We’ll be covering how to accomplish this using JavaScript or TypeScript. We’ll be utilizing a free external service API called pdfsignify.com, which simplifies the digital signing process and allows for easy personalization of signatures.
Digital signatures ensure the authenticity and integrity of your PDFs. To sign a PDF, you’ll need a digital certificate in the form of a
.pfx or .p12 file, which contains your private key and certificate. Additionally, you'll need the password associated with your certificate file.Part 1: Setting Up a Node.js Server
In this step, we’ll set up a new Node.js server that will generate and sign our PDFs. We’ll use Express for creating the server, Axios for making API requests to sign the PDFs, and fs (File System) to read the content of a PDF file.
If you already have your project set up and only need the server functionality, feel free to skip this part.
Step 1: Initialize Your Node.js Project
First, create a new directory for your project and navigate into it:
Copy
mkdir pdf-signing-server
cd pdf-signing-serverNext, initialize a new Node.js project:
Copy
npm init -yThis will create a
package.json file. Step 2: Install Required Packages
Now, install the required packages:
Copy
npm install express axios fs- Express: A minimal and flexible Node.js web application framework.
- Axios: A promise-based HTTP client for making API requests.
- fs: A built-in Node.js module for interacting with the file system.
Step 3: Create the Server
Create a new file called
server.js (or server.ts if you're using TypeScript). In this file, we'll set up a basic Express server.Copy
const express = require('express');
const fs = require('fs');
const axios = require('axios');
const app = express();
const port = 3000;
app.use(express.json());
// Basic route to test the server
app.get('/', (req, res) => {
res.send('PDF Signing Server is up and running!');
});
// Example route for generating and signing a PDF (to be implemented)
app.post('/sign-pdf', async (req, res) => {
// PDF generation and signing logic will go here
res.send('PDF signed successfully!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});This basic setup will get the server up and running. You can test it by running:
Copy
node server.jsThen, visit
http://localhost:3000 in your browser to confirm that the server is working
Part 2: Setting Up PDF Signify
Now that our Node.js server is up and running, it's time to set up PDF Signify to sign our PDFs. PDF Signify is a free service that allows us to easily add digital signatures to our PDF files. In this section, we'll guide you through the process of creating an account and preparing the service for use.
Step 1: Create an Account or Log In
1- Visit the PDFSignify Website: Go to the PDFSignify website at pdfsignify.com.

2- Register for an Account or Login: If you don't have an account yet, click on the "Register" button.
You have the option to sign up using Google or Microsoft accounts for a quick registration process. If you prefer manual registration, fill in your details (email, password, etc.) and complete the registration process.

Step 2: Verify Your Email
After registering, you will need to verify your email address to activate your account:
1- Check Your Inbox: Shortly after registering, you'll receive a verification email from PDFSignify.

2- Click the Verification Button: Open the email and click on the provided verification button or link to confirm your email address.

3- Didn't Receive the Email?: If you don't receive the email within a few minutes, check your spam or junk folder. If it's still not there, return to the PDFSignify dashboard and request another verification email.

Step 3: Create a New Project in PDFSignify
Now that you have verified your email and gained access to the PDFSignify dashboard, it's time to create a new project to begin working with the service. This will allow you to generate API keys, track usage, and manage your digital signatures.
1- Choose a Plan: Upon creating a new project, you'll be prompted to select a pricing plan. For our purposes, we will use the Free Plan, which comes with limitations but is sufficient for testing and basic usage.
The Free Plan typically includes a limited number of API calls per month, so keep this in mind during development.

2- Project Setup: After selecting the pricing plan, click next and give a name to your project.

Step 4: Explore the Admin Dashboard
With your project created, you can now view the Admin Dashboard. This is where you'll find important information and controls related to your project:
1- API Usage: The dashboard will show details about your API usage, including the number of API calls you've made and your remaining quota.
2- Limits: ou can also view your plan's limitations, such as the maximum number of API requests and any other restrictions associated with the Free Plan.
3- API Keys: In the dashboard, you'll find your API keys, which is essential for authenticating your requests from the Node.js server. Copy this key and keep it safe, as you'll need it in the next steps.

Part 3: Create API Credentials
Now that your project is set up in PDF Signify, the next step is to create API credentials. These credentials are essential for authenticating your requests to the PDF Signify API from your Node.js server. Follow the steps below to create and manage your API credentials.
Step 1: Navigate to the API Credentials Section
Locate and click on the "API Credentials" option in the sidebar. This is where you can create and manage your API keys.

Step 2: Create New API Credentials
In the API Credentials section, you'll see a "Create Credentials" button. Click on this button to generate a new set of API credentials.
After clicking the button, a popup will appear showing your new API credentials. These include:
- API Key:Used to authenticate your API requests.
- Secret Key:Used to sign your requests securely
The secret key will only be shown once, so make sure to copy and save it in a secure location. If you lose it, you will need to regenerate a new set of credentials.

Part 4: Coding the Node.js Application
Now that we have our PDF Signify API credentials, it's time to integrate them into our Node.js application. For this example, we'll use an already created PDF to save time.
If you need to generate a PDF first, you can use a library like Puppeteer or pdf-lib. However, once the PDF is generated, you can directly send its array buffer to PDF Signify without saving and reading the file again.
Step 1: Save the Already Created PDF
Before we move on to signing the PDF, let's save the PDF file that you'll be working with. This file will be located in the root directory of your Node.js project.
1- Obtain the PDF File: Ensure you have the PDF file that you want to sign. If you've generated the PDF elsewhere, make sure it's ready.
2- Save the PDF in the Root Directory: Move or copy your PDF file to the root directory of your Node.js project (the same directory where your
server.js file is located). Rename the file to filepdf.pdf.For example, your project directory structure should look like this:
Copy
pdf-signing-server/
│
├── server.js
├── package.json
├── node_modules/
└── filepdf.pdfStep 2: Add the dependencies
To ensure the necessary dependencies are imported at the top of your
server.js file, you should add the following lines of code.Copy
const fs = require('fs'); // Import fs module for file system operations
const axios = require('axios'); // Import Axios for making HTTP requests
const FormData = require('form-data'); // Import FormData for handling multipart form data- axios: This module is used for sending HTTP requests, which is crucial for interacting with the PDFSignify API.
- fs: The file system module is necessary for reading your PDF files, digital certificates, and any images you might need.
- FormData: This module allows you to construct multipart form data, which is needed to send the PDF and other files to the API.
Step 3: Create the signing route
To create a route in your Node.js application that signs a PDF and allows the client to download the signed PDF, you need to implement a new GET route in your
server.js.Here's how you can set up the GET route
/sign-pdf.Copy
app.get('/sign-pdf', async (req, res) => {
console.error('Sign my pdf');
});Step 4: Set Up Your Signing Certificate and Password
To sign PDFs using PDF Signify, you need a valid digital certificate and its associated password. This step involves placing your certificate in the project directory and configuring your application to use it.
Ensure you have your digital certificate file ready. This could be in
.pfx or .p12 format.Place your certificate file in the root directory of your Node.js project. Name it
certificate.pfx or certificate.p12.Your project directory should now include the certificate:
Copy
pdf-signing-server/
├── server.js
├── package.json
├── node_modules/
├── filepdf.pdf
└── certificate.pfx // or certificate.p12Step 5: Read the Necessary Files in the Code
To finalize the setup, you'll need to ensure that your code correctly reads the PDF and certificate files and securely manages the certificate password.
In your
/sign-pdf route, read the PDF and certificate files using the fs module. Here's how to integrate these into your existing function adding this:Copy
const cert = fs.readFileSync('certificate.pfx');
const pdf = fs.readFileSync('filepdf.pdf');
const password = 'YOUR_CERTIFICATE_PASSWORD';Step 6: Create the Form Data to Prepare the Request
To send a multipart/form-data request for signing a PDF, you'll need to prepare the form data correctly. This involves creating a
FormData object and appending the necessary fields, such as the certificate file, certificate password, and the PDF file to be signed.Here's how you can implement this:
Copy
// Import FormData for constructing the form data (not needed in client-side JavaScript)
const formData = new FormData();
// Append the digital certificate to the form data
formData.append('certificate', cert, {
filename: 'certificate.pfx',
contentType: 'application/x-pkcs12',
});
// Append the password for the digital certificate
formData.append('certificatePassword', 'password');
// Append the PDF document to the form data
formData.append('pdf', pdf, {
filename: 'filepdf.pdf',
contentType: 'application/pdf',
});Step 7: Execute the Request and Handle the Result
Now that we have set up the form data and included the necessary fields, it's time to execute the request to sign the PDF. We'll use axios to make the POST request to the PDFSignify API and handle the response.
Add this in the route:
Copy
try {
// Make a POST request using axios with form data
const response = await axios.post('https://api.pdfsignify.com/api/v1/sign-pdf', formData, {
headers: {
contentType: 'multipart/form-data', // Important: set content type to 'multipart/form-data'
'AccessKey': 'MY_ACCESS_KEY', // Your access key for the API
'SecretKey': 'MY_SECRET_KEY' // Your secret key for the API
},
responseType: 'arraybuffer' // The response will be in arrayBuffer format to handle the returned PDF
});
// Send the signed PDF as a response
res.contentType('application/pdf');
return res.send(response.data);
} catch (error) {
let errorResponse = error
if(error?.response?.data){
errorResponse = error.response.data.toString()
}
// Log any errors and return the error object
console.error('error',errorResponse);
res.send(errorResponse);
}IMPORTANT: In the request headers, replace
MY_ACCESS_KEY with the access key you generated on the PDFSignify website, and replace MY_SECRET_KEY with the secret key generated in your credentials.Now, you're
server.js file will look like:Copy
const express = require('express');
const fs = require('fs'); // Import fs module for file system operations
const axios = require('axios'); // Import Axios for making HTTP requests
const FormData = require('form-data'); // Import FormData for handling multipart form data
const app = express();
const port = 3000;
app.use(express.json());
// Basic route to test the server
app.get('/', (req, res) => {
res.send('PDF Signing Server is up and running!');
});
app.get('/sign-pdf', async (req, res) => {
// Read the digital certificate file (can be .pfx or .p12 format)
const cert = fs.readFileSync('certificate.pfx');
// Read the PDF document to be signed
const pdf = fs.readFileSync('filepdf.pdf');
// Import FormData for constructing the form data (not needed in client-side JavaScript)
const formData = new FormData();
// Append the digital certificate to the form data
formData.append('certificate', cert, {
filename: 'certificate.pfx',
contentType: 'application/x-pkcs12',
});
// Append the password for the digital certificate
formData.append('certificatePassword', 'password');
// Append the PDF document to the form data
formData.append('pdf', pdf, {
filename: 'filepdf.pdf',
contentType: 'application/pdf',
});
// Send the request to sign the PDF document
try {
// Make a POST request using axios with form data
const response = await axios.post('https://api.pdfsignify.com/api/v1/sign-pdf', formData, {
headers: {
contentType: 'multipart/form-data', // Important: set content type to 'multipart/form-data'
'AccessKey': 'MY_ACCESS_KEY', // Your access key for the API
'SecretKey': 'MY_SECRET_KEY' // Your secret key for the API
},
responseType: 'arraybuffer' // The response will be in arrayBuffer format to handle the returned PDF
});
// Send the signed PDF as a response
res.contentType('application/pdf');
return res.send(response.data);
} catch (error) {
let errorResponse = error
if(error?.response?.data){
errorResponse = error.response.data.toString()
}
// Log any errors and return the error object
console.error('error',errorResponse);
res.send(errorResponse);
}
});
// Example route for generating and signing a PDF (to be implemented)
app.post('/sign-pdf', async (req, res) => {
// PDF generation and signing logic will go here
res.send('PDF signed successfully!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);Step 8: Rerun the Server and Test the Sign Route
Now it's time to test your implementation.
Restart your server to apply the changes. Since this basic server setup does not include live reloading, you'll need to manually restart the server with the command node
server.jsOpen your web browser and navigate to the sign route by entering the following URL
sign-pdf route http://localhost:3000/sign-pdfPart 5: Check the results
Error Response
If you encounter an error, you will see a message indicating the issue and its cause. Common errors include:
1. PDF not saved with the correct name: Ensure the file name matches the required format.
2. Certificate not saved with the correct name: Verify both the file name and extension are correct.
3. Incorrect certificate password: Double-check that the password you entered is correct.
4. Incorrect API credentials: Review your API credentials and regenerate them if necessary.
Success Response
Once the route is opened, you will see the signed PDF. However, the signatures may not appear in the browser.
To view the signatures, download the PDF and open it with a PDF viewer on your operating system. On Windows and macOS, you can use
Adobe Reader, while on Linux, you can use the default Document Viewer .The signature will be visible at coordinates (0,0), which is located at the bottom left corner of the page.

Part 6: Customize the Signature
Pdfsignify offers a wide range of features to customize your signature. In this section, we'll explore many of the available customization options.
For a complete list of features and options, you can refer to the documentation here.
Step 1: Background Image Customization:
To customize the background image, a payment account is required. Once you have a payment account, you can set your logo as the background image by using the
signatureBackgroundImage parameter.This involves sending the image as a buffer for the customization:
Copy
const backgroundImage = fs.readFileSync('backgroundImage.png');
formData.append('signatureBackgroundImage', backgroundImage, {
filename: 'logo.png',
contentType: 'image/png',
});Step 2: Image Near The Signature:
If you don't have a paid account, you can still display your logo by using the
signatureImage parameter. This allows you to place an image near the signature.Copy
const signatureImage = fs.readFileSync('signatureImage.png')
formData.append('signatureImage', signatureImage, {
filename: 'logo.png',
contentType: 'image/png',
});Step 3: Signature parameters
In the documentation, you can find various parameters to customize the signature display and its values:
timezone: Timezone for the signature timestamp in PHP format. Default is UTC. View the list of all available timezonessignatureFieldName: Unique field name for the signature (default will set an automatically unique name)signatureXPosition: Set the X position of the signature in pixels relative to the left edge of the PDF. A value of 0 corresponds to the far left. (Default is 0).signatureYPosition: Set the Y position of the signature in pixels relative to the bottom of the PDF. The value of 0 corresponds to the very bottom of the PDF. (Default is 0)signatureWidth: Width in pixels of the signature (default 100)signatureHeight: Height in pixels of the signature (default 50)signatureMessage: Visual message label for the signature (default 'Signed digitally')signaturePageAppearance: Page(s) which will appear the signature. Can be an integer to specify a single page, an array of integers to specify multiple pages, or -1 to signify all pages. Default is 1.signatureDateLabel: Label for the signature date (default 'Date: ')signatureDateFormat: Format for the signature date in PHP format (default 'd-m-Y H:i:s')signarureReasonLabel: Label for the signature reason (default 'Reason:')signatureReason: Reason for the signature (default null)signatureLocation: Location of the signature (default null)signatureContactInfo: Contract info for the signature (default null)signatureShowDistinguishedName: Show the Distinguished Name (DN) in the signature, including the proprietary name and all information related to the signature (default: false).You can find some examples in the following code:
Copy
// Customize signature appearance and position
formData.append('signaturePageAppearance', -1); // Page number for signature (-1 for all pages or specify an array of pages)
formData.append('timezone', 'UTC'); // Timezone for signature date and time
formData.append('signatureMessage', 'Digitally signed by the user'); // Reason/message for the signature
formData.append('signatureDateLabel', ''); // Label for the signature date (empty string for no label)
formData.append('signatureDateFormat', 'Y-m-d H:i:s'); // Format for the signature date
formData.append('signatureHeight', 100); // Signature height in pixels
formData.append('signatureWidth', 150); // Signature width in pixels
formData.append('signatureYPosition', 100); // Y position of the signature relative to the bottom of the page
formData.append('signatureXPosition', 180); // X position of the signature relative to the left of the pageStep 4: PDF Metadata
When you sign a PDF, PDF Signify also allows you to modify the metadata. The following parameters are available for this purpose:
pdfMetadataTitle: Title of the PDF document.pdfMetadataSubject: Subject of the PDF document.pdfMetadataAuthor: Author of the PDF document.pdfMetadataKeywords: Keywords associated with the PDF document.pdfMetadataCreator: Creator application of the PDF document.pdfMetadataProducer: Producer application of the PDF document.pdfMetadataCreationDate: Creation date of the PDF document in ISO 8601 format (e.g.,2024–01–01 10:00:00).pdfMetadataModificationDate: Last modification date of the PDF document in ISO 8601 format (e.g.,2024–01–01 10:00:00).Copy
// Optional: Modify PDF metadata (e.g., author, title, keywords)
formData.append('pdfMetadataAuthor', 'AUTHOR'); // PDF author
formData.append('pdfMetadataKeywords', 'keywords,keyword2'); // PDF keywords
formData.append('pdfMetadataTitle', 'MY DOCUMENT'); // PDF title
formData.append('pdfMetadataSubject', 'EXAMPLE DOCUMENT'); // PDF subject
formData.append('pdfMetadataCreator', 'PDFSIGNIFY'); // PDF creator software
formData.append('pdfMetadataProducer', 'PDFSIGNIFY'); // PDF producer software
formData.append('pdfMetadataCreationDate', new Date().toLocaleString()); // Creation date
formData.append('pdfMetadataModificationDate', new Date().toLocaleString()); // Modification dateIMPORTANT: You can also use the
/api/v1/set-pdf-metadata endpoint with the same parameters to change the PDF metadata without needing to sign the document. You can find more information here.You can find the complete file
server.jswith all the examples:Copy
const express = require('express');
const fs = require('fs'); // Import fs module for file system operations
const axios = require('axios'); // Import Axios for making HTTP requests
const FormData = require('form-data'); // Import FormData for handling multipart form data
const app = express();
const port = 3000;
app.use(express.json());
// Basic route to test the server
app.get('/', (req, res) => {
res.send('PDF Signing Server is up and running!');
});
app.get('/sign-pdf', async (req, res) => {
// Read the digital certificate file (can be .pfx or .p12 format)
const cert = fs.readFileSync('certificate.pfx');
// Read the PDF document to be signed
const pdf = fs.readFileSync('filepdf.pdf');
// Import FormData for constructing the form data (not needed in client-side JavaScript)
const formData = new FormData();
// Append the digital certificate to the form data
formData.append('certificate', cert, {
filename: 'certificate.pfx',
contentType: 'application/x-pkcs12',
});
// Append the password for the digital certificate
formData.append('certificatePassword', 'password');
// Append the PDF document to the form data
formData.append('pdf', pdf, {
filename: 'filepdf.pdf',
contentType: 'application/pdf',
});
// Append the custom background image to the form data (optional)
const backgroundImage = fs.readFileSync('signatureBackgroundImage.png');
formData.append('signatureBackgroundImage', backgroundImage, {
filename: 'logo.png',
contentType: 'image/png',
});
// // Append the custom image near the signature (optional)
// const signatureImage = fs.readFileSync('signatureImage.png')
// formData.append('signatureImage', signatureImage, {
// filename: 'logo.png',
// contentType: 'image/png',
// });
// Customize signature appearance and position
formData.append('signaturePageAppearance', -1); // Page number for signature (-1 for all pages or specify an array of pages)
formData.append('timezone', 'UTC'); // Timezone for signature date and time
formData.append('signatureMessage', 'Digitally signed by the user'); // Reason/message for the signature
formData.append('signatureDateLabel', ''); // Label for the signature date (empty string for no label)
formData.append('signatureDateFormat', 'Y-m-d H:i:s'); // Format for the signature date
formData.append('signatureHeight', 100); // Signature height in pixels
formData.append('signatureWidth', 150); // Signature width in pixels
formData.append('signatureYPosition', 100); // Y position of the signature relative to the bottom of the page
formData.append('signatureXPosition', 180); // X position of the signature relative to the left of the page
// Optional: Modify PDF metadata (e.g., author, title, keywords)
formData.append('pdfMetadataAuthor', 'AUTHOR'); // PDF author
formData.append('pdfMetadataKeywords', 'keywords,keyword2'); // PDF keywords
formData.append('pdfMetadataTitle', 'MY DOCUMENT'); // PDF title
formData.append('pdfMetadataSubject', 'EXAMPLE DOCUMENT'); // PDF subject
formData.append('pdfMetadataCreator', 'PDFSIGNIFY'); // PDF creator software
formData.append('pdfMetadataProducer', 'PDFSIGNIFY'); // PDF producer software
formData.append('pdfMetadataCreationDate', new Date().toLocaleString()); // Creation date
formData.append('pdfMetadataModificationDate', new Date().toLocaleString()); // Modification date
// Send the request to sign the PDF document
try {
// Make a POST request using axios with form data
const response = await axios.post('https://api.pdfsignify.com/api/v1/sign-pdf', formData, {
headers: {
contentType: 'multipart/form-data', // Important: set content type to 'multipart/form-data'
'AccessKey': 'MY_ACCESS_KEY', // Your access key for the API
'SecretKey': 'MY_SECRET_KEY' // Your secret key for the API
},
responseType: 'arraybuffer' // The response will be in arrayBuffer format to handle the returned PDF
});
// Send the signed PDF as a response
res.contentType('application/pdf');
return res.send(response.data);
} catch (error) {
let errorResponse = error
if(error?.response?.data){
errorResponse = error.response.data.toString()
}
// Log any errors and return the error object
console.error('error',errorResponse);
res.send(errorResponse);
}
});
// Example route for generating and signing a PDF (to be implemented)
app.post('/sign-pdf', async (req, res) => {
// PDF generation and signing logic will go here
res.send('PDF signed successfully!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});Step 5: Verify Certificate Validity and Password Match
PDF Signify also allows you to verify if a certificate is valid and if the password matches.
To perform this check, use the
/api/v1/check-certificate-password endpoint. You can find more information here.This method uses only two parameters: the certificate (
certificate), as used previously, and the certificate password (certificatePassword).You can use the following example with the (
/check-certificate)endpoint:Copy
app.get('/check-certificate', async (req, res) => {
// Read the digital certificate file (can be .pfx or .p12 format)
const cert = fs.readFileSync('certificate.pfx');
// Import FormData for constructing the form data (not needed in client-side JavaScript)
const formData = new FormData();
// Append the digital certificate to the form data
formData.append('certificate', cert, {
filename: 'certificate.pfx',
contentType: 'application/x-pkcs12',
});
// Append the password for the digital certificate
formData.append('certificatePassword', 'password');
// Send the request to sign the PDF document
try {
// Make a POST request using axios with form data
const response = await axios.post('https://api.pdfsignify.com/api/v1/check-certificate-password', formData, {
headers: {
contentType: 'multipart/form-data', // Important: set content type to 'multipart/form-data'
'AccessKey': 'MY_ACCESS_KEY', // Your access key for the API
'SecretKey': 'MY_SECRET_KEY' // Your secret key for the API
},
});
return res.send(response.data);
} catch (error) {
let errorResponse = error
if(error?.response?.data){
errorResponse = error.response.data
}
// Log any errors and return the error object
console.error('error',errorResponse);
res.send(errorResponse);
}
});In this case, the response will be in
application/json format and may indicate success as follows:Copy
{'success':true,'message':'The certificate password is correct!','errors':[],'data':{},'auth':true}Alternatively, the response may indicate that the certificate or password is not valid, as shown below:
Copy
{'success':false,'message':'The certificate password is not correct!','errors':['certificatePassword'],'data':{},'auth':true}Part 7: Conclusions
In conclusion, we have explored a comprehensive example of how to sign PDFs using Node.js and the PDF Signify application. We've seen how this service can assist in signing any PDF document, setting PDF metadata, or verifying a certificate with ease, and it can be integrated with various technologies.
For more information, you can visit the following resources:
- API Documentation: https://api.pdfsignify.com/api/documentation#/
- Examples: https://pdfsignify.com/#examples
- Website: https://pdfsignify.com/
If you have further questions, you can contact PDF Signify support via email.