Back to Blog
APITutorial

eSignature API Tutorial: Build Your First Integration

PDFSignify TeamMarch 30, 202614 min read

This tutorial walks you through building a complete PDF signing integration using the PDFSignify API. By the end, you'll have working code that signs PDFs with a digital certificate, customizes the signature appearance, and sets document metadata — all in a single API call.

We'll use Node.js for the examples, but the API is a standard REST service that works with any language or HTTP client.

Prerequisites

  • A PDFSignify account — sign up at pdfsignify.com to get your AccessKey and SecretKey
  • A .pfx or .p12 digital certificate (you can use a self-signed certificate for testing)
  • Node.js 18+ installed (or any language with an HTTP client)
  • A PDF file to sign

Step 1: Set Up Your Project

Create a new project directory and install the two dependencies we need: axios for HTTP requests and form-data for building multipart requests.

bash
mkdir pdf-signing-demo && cd pdf-signing-demo
npm init -y
npm install axios form-data

Place your certificate file (certificate.pfx) and a test PDF (document.pdf) in the project directory. Then create your main script file:

javascript
// sign.mjs
import axios from "axios";
import * as fs from "fs";
import FormData from "form-data";

const ACCESS_KEY = process.env.PDFSIGNIFY_ACCESS_KEY;
const SECRET_KEY = process.env.PDFSIGNIFY_SECRET_KEY;
const CERT_PASSWORD = process.env.CERT_PASSWORD;

if (!ACCESS_KEY || !SECRET_KEY) {
  console.error("Missing API credentials. Set PDFSIGNIFY_ACCESS_KEY and PDFSIGNIFY_SECRET_KEY.");
  process.exit(1);
}

Step 2: Validate Your Certificate

Before signing anything, let's verify that the certificate and password are valid. This is especially useful when users upload their own certificates — you can catch errors early instead of failing mid-batch.

javascript
async function validateCertificate(certPath, certPassword) {
  const formData = new FormData();
  formData.append("certificate", fs.readFileSync(certPath), {
    filename: "certificate.pfx",
    contentType: "application/x-pkcs12"
  });
  formData.append("certificatePassword", certPassword);

  const response = await axios.post(
    "https://api.pdfsignify.com/api/v1/check-certificate-password",
    formData,
    {
      headers: {
        ...formData.getHeaders(),
        "AccessKey": ACCESS_KEY,
        "SecretKey": SECRET_KEY
      }
    }
  );

  return response.data.success;
}

const isValid = await validateCertificate("certificate.pfx", CERT_PASSWORD);
if (!isValid) {
  console.error("Invalid certificate or password.");
  process.exit(1);
}
console.log("Certificate validated successfully.");

Step 3: Sign Your First PDF

Now let's sign a PDF. The sign-pdf endpoint accepts multipart/form-data with three required fields: the certificate file, the certificate password, and the PDF to sign. The response body is the signed PDF as binary data.

javascript
async function signPdf(pdfPath, certPath, certPassword) {
  const formData = new FormData();
  formData.append("certificate", fs.readFileSync(certPath), {
    filename: "certificate.pfx",
    contentType: "application/x-pkcs12"
  });
  formData.append("certificatePassword", certPassword);
  formData.append("pdf", fs.readFileSync(pdfPath), {
    filename: "document.pdf",
    contentType: "application/pdf"
  });

  const response = await axios.post(
    "https://api.pdfsignify.com/api/v1/sign-pdf",
    formData,
    {
      headers: {
        ...formData.getHeaders(),
        "AccessKey": ACCESS_KEY,
        "SecretKey": SECRET_KEY
      },
      responseType: "arraybuffer"
    }
  );

  return Buffer.from(response.data);
}

const signedPdf = await signPdf("document.pdf", "certificate.pfx", CERT_PASSWORD);
fs.writeFileSync("signed-document.pdf", signedPdf);
console.log("PDF signed and saved to signed-document.pdf");

Step 4: Customize the Signature Appearance

By default, PDFSignify applies a minimal visible signature. You can customize every aspect of it: the position, size, message text, date format, timezone, and even a background image (like a company logo or a handwritten signature scan).

javascript
async function signPdfWithCustomSignature(pdfPath, certPath, certPassword) {
  const formData = new FormData();
  formData.append("certificate", fs.readFileSync(certPath), {
    filename: "certificate.pfx",
    contentType: "application/x-pkcs12"
  });
  formData.append("certificatePassword", certPassword);
  formData.append("pdf", fs.readFileSync(pdfPath), {
    filename: "document.pdf",
    contentType: "application/pdf"
  });

  // Signature customization
  formData.append("signatureMessage", "Digitally signed by ACME Corp");
  formData.append("signatureDateLabel", "Date:");
  formData.append("signatureDateFormat", "Y-m-d H:i:s");
  formData.append("timezone", "Europe/London");
  formData.append("signaturePageAppearance", "-1"); // Show on all pages
  formData.append("signatureXPosition", "350");
  formData.append("signatureYPosition", "50");
  formData.append("signatureWidth", "200");
  formData.append("signatureHeight", "80");

  // Optional: add a background image
  if (fs.existsSync("company-logo.png")) {
    formData.append("signatureBackgroundImage", fs.readFileSync("company-logo.png"), {
      filename: "company-logo.png",
      contentType: "image/png"
    });
  }

  const response = await axios.post(
    "https://api.pdfsignify.com/api/v1/sign-pdf",
    formData,
    {
      headers: {
        ...formData.getHeaders(),
        "AccessKey": ACCESS_KEY,
        "SecretKey": SECRET_KEY
      },
      responseType: "arraybuffer"
    }
  );

  return Buffer.from(response.data);
}

Step 5: Set PDF Metadata

You can also update a PDF's metadata without signing it. This is useful for tagging documents before archiving, or for adding organizational information to generated PDFs.

javascript
async function setPdfMetadata(pdfPath, metadata) {
  const formData = new FormData();
  formData.append("pdf", fs.readFileSync(pdfPath), {
    filename: "document.pdf",
    contentType: "application/pdf"
  });

  for (const [key, value] of Object.entries(metadata)) {
    formData.append(key, value);
  }

  const response = await axios.post(
    "https://api.pdfsignify.com/api/v1/set-pdf-metadata",
    formData,
    {
      headers: {
        ...formData.getHeaders(),
        "AccessKey": ACCESS_KEY,
        "SecretKey": SECRET_KEY
      },
      responseType: "arraybuffer"
    }
  );

  return Buffer.from(response.data);
}

const taggedPdf = await setPdfMetadata("document.pdf", {
  pdfMetadataAuthor: "ACME Corporation",
  pdfMetadataTitle: "Service Agreement",
  pdfMetadataSubject: "Legal",
  pdfMetadataKeywords: "contract,agreement,2026"
});
fs.writeFileSync("tagged-document.pdf", taggedPdf);

Step 6: Build a Reusable Signing Service

In a production application, you'll want to wrap the API calls in a reusable service class. Here's a pattern that works well for Express or Fastify backends:

javascript
class PdfSigningService {
  constructor(accessKey, secretKey) {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
    this.baseUrl = "https://api.pdfsignify.com/api/v1";
  }

  async sign(pdfBuffer, certBuffer, certPassword, options = {}) {
    const formData = new FormData();
    formData.append("certificate", certBuffer, {
      filename: "certificate.pfx",
      contentType: "application/x-pkcs12"
    });
    formData.append("certificatePassword", certPassword);
    formData.append("pdf", pdfBuffer, {
      filename: "document.pdf",
      contentType: "application/pdf"
    });

    for (const [key, value] of Object.entries(options)) {
      formData.append(key, value);
    }

    const response = await axios.post(
      this.baseUrl + "/sign-pdf",
      formData,
      {
        headers: {
          ...formData.getHeaders(),
          "AccessKey": this.accessKey,
          "SecretKey": this.secretKey
        },
        responseType: "arraybuffer"
      }
    );

    return Buffer.from(response.data);
  }
}

// Usage
const signer = new PdfSigningService(ACCESS_KEY, SECRET_KEY);
const signed = await signer.sign(pdfBuffer, certBuffer, "password", {
  signatureMessage: "Approved",
  signaturePageAppearance: "-1"
});

Step 7: Handle Errors Gracefully

The PDFSignify API returns standard HTTP status codes. Wrap your calls in try/catch blocks and handle common failure scenarios:

javascript
try {
  const signedPdf = await signPdf("document.pdf", "certificate.pfx", CERT_PASSWORD);
  fs.writeFileSync("signed.pdf", signedPdf);
} catch (error) {
  if (error.response) {
    const status = error.response.status;
    const body = error.response.data.toString();
    if (status === 401) {
      console.error("Authentication failed. Check your AccessKey and SecretKey.");
    } else if (status === 400) {
      console.error("Bad request:", body);
    } else {
      console.error("API error (" + status + "):", body);
    }
  } else {
    console.error("Network error:", error.message);
  }
}

Running the Complete Script

Set your environment variables and run the script:

bash
export PDFSIGNIFY_ACCESS_KEY="your_access_key"
export PDFSIGNIFY_SECRET_KEY="your_secret_key"
export CERT_PASSWORD="your_cert_password"

node sign.mjs

You should see "PDF signed and saved to signed-document.pdf" in your terminal. Open the signed PDF in any PDF reader — you'll see the digital signature in the signature panel, confirming the document's integrity and the signer's identity.

Integrating Into a Web Application

In a real application, the flow typically looks like this: your backend receives a PDF (from a file upload, a document generator, or a database), reads the certificate from a secure store, calls the PDFSignify API, and then stores or serves the signed PDF. Since the API is synchronous, your endpoint can return the signed document directly to the user.

javascript
// Express route example
app.post("/api/sign", upload.single("pdf"), async (req, res) => {
  const certBuffer = await getSecretFromVault("signing-certificate");
  const certPassword = await getSecretFromVault("cert-password");

  const formData = new FormData();
  formData.append("certificate", certBuffer, {
    filename: "certificate.pfx",
    contentType: "application/x-pkcs12"
  });
  formData.append("certificatePassword", certPassword);
  formData.append("pdf", req.file.buffer, {
    filename: req.file.originalname,
    contentType: "application/pdf"
  });

  const response = await axios.post(
    "https://api.pdfsignify.com/api/v1/sign-pdf",
    formData,
    {
      headers: {
        ...formData.getHeaders(),
        "AccessKey": process.env.PDFSIGNIFY_ACCESS_KEY,
        "SecretKey": process.env.PDFSIGNIFY_SECRET_KEY
      },
      responseType: "arraybuffer"
    }
  );

  res.set("Content-Type", "application/pdf");
  res.set("Content-Disposition", "attachment; filename=signed-" + req.file.originalname);
  res.send(Buffer.from(response.data));
});

What's Next?

  • Batch signing — loop through multiple PDFs and sign each one sequentially or in parallel
  • Certificate management — store certificates in AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault
  • Signature templates — pre-configure signature appearance options for different document types
  • Audit logging — record every signing operation with timestamps and document hashes for compliance

The PDFSignify API is stateless and synchronous, which makes it easy to integrate into any architecture — from serverless functions to monolithic backends. Start simple, sign one PDF, and build from there.

Start simple, then scale. One API call to sign a PDF — that's all it takes to get started.