Skip to content

JavaScript SDK Complete Guide

Complete integration guide for the eSign JavaScript SDK.


Overview

The eSign JavaScript SDK provides a simple, promise-based API for integrating Aadhaar-based digital signing into web applications.

Features

  • Browser & Node.js - Works in both environments
  • TypeScript Support - Full type definitions included
  • Promise-based - Modern async/await syntax
  • Builder Pattern - Fluent API for requests
  • Zero Dependencies - No external dependencies (browser)
  • 5 Auth Modes - OTP, Biometric, Iris, Face, eKYC

Architecture

sequenceDiagram
    participant App as Your App
    participant SDK as JS SDK
    participant API as eSign API Server
    participant ESP as Capricorn ESP
    participant UIDAI as UIDAI

    App->>SDK: signDocument(request)
    SDK->>API: POST /api/java/v1/esign
    API->>ESP: Initialize signing
    ESP-->>API: redirectUrl
    API-->>SDK: Response
    SDK-->>App: {redirectUrl, reference}

    App->>ESP: Open redirectUrl
    ESP->>UIDAI: Send OTP
    UIDAI-->>User: OTP to mobile
    User->>ESP: Enter OTP
    ESP->>UIDAI: Verify
    UIDAI-->>ESP: Certificate
    ESP-->>API: Signed PDF

    App->>SDK: downloadSignedDocument(ref)
    SDK->>API: GET /document/{ref}?token=SHORT_LIVED_TOKEN
    API-->>SDK: PDF bytes
    SDK-->>App: Signed PDF

Server Required

The SDK is a client library. You must deploy the eSign API server on your backend.


Installation

Browser

<!-- Option 1: Local file -->
<script src="path/to/esign-sdk.min.js"></script>

<!-- Option 2: CDN (if available) -->
<script src="https://cdn.example.com/esign-sdk.min.js"></script>

Node.js

// CommonJS
const { ESignClient, ESignRequest, ESignMode } = require('./esign-sdk.min.js');

// ES Modules
import { ESignClient, ESignRequest, ESignMode } from './esign-sdk.min.js';

TypeScript

TypeScript definitions are included. No additional installation needed.

import {
    ESignClient,
    ESignRequest,
    SigningOptions,
    ESignMode,
    SignDocumentResponse
} from './esign-sdk.min.js';

API Reference

ESignClient

The main client class for interacting with the eSign API.

Constructor

const client = new ESignClient({
    apiBaseUrl: string,    // Required - Your eSign API server URL
    token: string,         // Required - API token from Capricorn
    key: string,           // Required - API key from Capricorn
    timeout: number        // Optional - Request timeout in ms (default: 60000)
});

Methods

Method Returns Description
signDocument(request) Promise<SignDocumentResponse> Initiate document signing
getStatus(reference) Promise<StatusResponse> Secure lookup (status + pdfurl)
downloadSignedDocument(ref) Promise<ArrayBuffer\|Buffer> Download as bytes
downloadSignedDocumentBase64(ref) Promise<string> Download as Base64
downloadSignedDocumentBlob(ref) Promise<Blob> Download as Blob (browser)
checkHealth() Promise<HealthResponse> Check API health

ESignRequest

Builder class for creating signing requests.

const request = new ESignRequest()
    .pdf64(string)              // Base64 encoded PDF (required*)
    .pdfUrl(string)             // URL to fetch PDF (required*)
    .title(string)              // Document title (required)
    .mode(ESignMode)            // Authentication mode (required)
    .signerName(string)         // Signer name (required)
    .txn(string)                // Transaction ID (optional)
    .ekycId(string)             // eKYC ID (required for eKYC mode)
    .callbackUrl(string)        // Webhook URL (optional)
    .options(SigningOptions)    // Signing options (optional)
    .build();                   // Validate and build

// * Either pdf64 OR pdfUrl is required

SigningOptions

Builder class for configuring signature appearance.

const options = new SigningOptions()
    // Position (x1, y1, x2, y2) - origin at bottom-left
    .coordinates(350, 50, 550, 120)

    // Or as string
    .coordinates('350,50,550,120')

    // Page selection
    .pageNum('1')           // Specific page
    .pageNum('1-3')         // Range
    .pageNum('1,3,5')       // Multiple pages
    .allPages()             // All pages
    .firstPage()            // First page only
    .lastPage()             // Last page only

    // Signature text
    .reason('Digital Signature')
    .location('India')
    .customText('Approved')

    // Appearance
    .greenTicked(true)      // Show ✓ icon
    .showGreenTick()        // Same as above
    .hideGreenTick()        // Hide ✓ icon
    .dateFormat('dd-MMM-yyyy hh:mm a')

    // PDF protection
    .lockPdf(LockMode.NO_LOCK)

    // Multi-location signing (different positions on different pages)
    .signaturePositions([
        { pages: 'first', cood: '50,50,200,120' },
        { pages: 'last', cood: '400,50,550,120' }
    ])

    // Or add positions one at a time
    .addPosition('first', 50, 50, 200, 120)
    .addPosition('last', '400,50,550,120')
    .addPosition('2,3', 200, 400, 400, 470)

    // Clear all positions
    .clearPositions()

    .build();

Multi-Location Methods

Method Description
.signaturePositions(array) Set array of {pages, cood} positions
.addPosition(pages, x1, y1, x2, y2) Add a position with coordinates
.addPosition(pages, 'x1,y1,x2,y2') Add a position with coordinate string
.clearPositions() Remove all multi-location positions

Multi-Location Auto-Settings

When using .signaturePositions() or .addPosition(), the SDK automatically sets cood and pagenum to "custom".

Search By Text (Global Auto-Placement)

Use API-level options to enable text-based placement across all pages:

const options = {
    pagenum: 'searchbytext',
    cood: 'searchbytext',
    searchbytext: 'Authorized Signatory',
    boxsize: '220,80',
    boxposition: 'bottom'
};

const request = new ESignRequest()
    .pdf64(base64Pdf)
    .title('Contract')
    .mode(ESignMode.OTP)
    .signerName('John Doe')
    .options(options)
    .build();

Note: boxsize and boxposition are required in this mode, and manual positions are ignored.

Multi-Location Example

// Different signature positions on different pages
const options = new SigningOptions()
    .addPosition('first', 50, 50, 200, 120)      // Bottom-left on first page
    .addPosition('2,3,4', 200, 400, 400, 470)    // Center on pages 2-4
    .addPosition('last', 400, 50, 550, 120)      // Bottom-right on last page
    .reason('Digital Signature')
    .location('India')
    .build();

const request = new ESignRequest()
    .pdf64(base64Pdf)
    .title('Multi-Location Contract')
    .mode(ESignMode.OTP)
    .signerName('John Doe')
    .options(options)
    .build();

ESignMode

Authentication modes enum.

Mode Value Description Requirements
ESignMode.OTP online-aadhaar-otp OTP authentication None
ESignMode.BIO online-aadhaar-bio Fingerprint Biometric device
ESignMode.FINGERPRINT online-aadhaar-bio Alias for BIO Biometric device
ESignMode.IRIS online-aadhaar-iris Iris scan Iris scanner
ESignMode.FACE online-aadhaar-face Face recognition Camera
ESignMode.CAPRICORN_EKYC capricorn-ekyc-account Pre-verified KYC ekycId

Helper Methods

// Check if mode requires eKYC ID
ESignMode.requiresEkycId(ESignMode.CAPRICORN_EKYC);  // true

// Check if mode requires device
ESignMode.requiresDevice(ESignMode.BIO);  // true

// Get eSign specification version
ESignMode.getVersion(ESignMode.OTP);  // "2.1"
ESignMode.getVersion(ESignMode.CAPRICORN_EKYC);  // "3.2"

// Parse mode from string
ESignMode.fromString('otp');  // ESignMode.OTP

LockMode

PDF protection modes.

Mode Value Description
LockMode.NO_LOCK n Allow modifications
LockMode.CERTIFIED y No changes allowed
LockMode.CERTIFIED_FORM_FILLING cf Allow form filling only
LockMode.CERTIFIED_FORM_ANNOTATIONS cfa Allow forms & annotations
LockMode.SIGNATURE_LOCK ym Lock signature dictionary

ESignUtils

Utility functions for common operations.

// Convert File to Base64 (browser only)
const base64 = await ESignUtils.fileToBase64(file);

// Convert Base64 to Blob (browser only)
const blob = ESignUtils.base64ToBlob(base64, 'application/pdf');

// Download Blob as file (browser only)
ESignUtils.downloadBlob(blob, 'document.pdf');

// Open PDF in new tab (browser only)
ESignUtils.openPdfInNewTab(blob);

// Generate unique transaction ID
const txnId = ESignUtils.generateTxnId();
// Returns: "TXN-1704067200000-abc123xyz"

Response Objects

SignDocumentResponse

{
    success: true,
    requestId: "REQ-123456",
    date: "2025-12-15T10:30:00",
    txn: "TXN-1704067200000",
    reference: "ESP-ABC123XYZ",
    redirectUrl: "https://esign.capricorn.com/auth?ref=ESP-ABC123XYZ",
    getSignedDocUrl: "https://api.example.com/api/java/v1/esign/signed/ESP-ABC123XYZ?token=SHORT_LIVED_TOKEN"
}

StatusResponse

{
    reference: "ESP-ABC123XYZ",
    txn: "TXN-1704067200000",
    status: "COMPLETED",  // PENDING | INITIATED | COMPLETED | FAILED | EXPIRED
    signerName: "John Doe",
    createdAt: "2025-12-15T10:30:00",
    updatedAt: "2025-12-15T10:35:00",
    completedAt: "2025-12-15T10:35:00",
    pdfUrl: "https://api.example.com/api/java/v1/esign/document/ESP-ABC123XYZ?token=SHORT_LIVED_TOKEN",
    signPdf64: "JVBERi0xLjQK...",

    // Helper methods
    isCompleted(): boolean,
    isPending(): boolean,
    isFailed(): boolean
}

Coordinate System

The PDF coordinate system uses points (1 point = 1/72 inch) with origin at bottom-left.

(0, 842) +---------------------------------------+ (595, 842)
         |                TOP                    |
         |                                       |
         |        A4: 595 x 842 points           |
         |                                       |
         |          Signature Box                |
         |          (x1,y1) to (x2,y2)           |
         |                                       |
         |               BOTTOM                  |
(0, 0)   +---------------------------------------+ (595, 0)

Common Signature Positions

Position Coordinates Usage
Bottom-right 400,50,550,120 Most common
Bottom-left 50,50,200,120 Alternative
Bottom-center 200,50,400,120 Centered
Top-right 400,750,550,820 Header area
Top-left 50,750,200,820 Header area

Complete Examples

Example 1: Basic OTP Signing

const { ESignClient, ESignRequest, ESignMode, ESignUtils } = ESignSDK;

// Initialize client
const client = new ESignClient({
    apiBaseUrl: 'https://your-api.com',
    token: 'your-token',
    key: 'your-key'
});

async function signWithOTP(pdfFile, signerName) {
    // Convert file to Base64
    const pdf64 = await ESignUtils.fileToBase64(pdfFile);

    // Create and send request
    const response = await client.signDocument(
        new ESignRequest()
            .pdf64(pdf64)
            .title('Contract Agreement')
            .mode(ESignMode.OTP)
            .signerName(signerName)
    );

    console.log('Reference:', response.reference);

    // Open OTP authentication page
    window.open(response.redirectUrl, '_blank');

    return response.reference;
}

Example 2: eKYC Mode Signing

async function signWithEKYC(pdf64, signerName, ekycId) {
    const response = await client.signDocument(
        new ESignRequest()
            .pdf64(pdf64)
            .title('KYC Document')
            .mode(ESignMode.CAPRICORN_EKYC)
            .ekycId(ekycId)  // Required for eKYC mode!
            .signerName(signerName)
    );

    return response;
}

Example 3: Custom Signing Options

const options = new SigningOptions()
    .coordinates(50, 700, 250, 780)  // Top-left
    .lastPage()
    .reason('Approved by Manager')
    .location('Mumbai, India')
    .customText('Final Approval')
    .showGreenTick()
    .dateFormat('dd-MMM-yyyy hh:mm a')
    .lockPdf(LockMode.CERTIFIED);

const response = await client.signDocument(
    new ESignRequest()
        .pdf64(pdf64)
        .title('Approval Document')
        .mode(ESignMode.OTP)
        .signerName('Manager Name')
        .options(options)
);

Example 4: Status Polling

async function waitForCompletion(reference, maxAttempts = 20) {
    for (let i = 0; i < maxAttempts; i++) {
        const status = await client.getStatus(reference);

        console.log(`Attempt ${i + 1}: ${status.status}`);

        if (status.isCompleted()) {
            return status;
        }

        if (status.isFailed()) {
            throw new Error('Signing failed');
        }

        // Wait 3 seconds before next check
        await new Promise(r => setTimeout(r, 3000));
    }

    throw new Error('Timeout waiting for completion');
}

// Usage
const status = await waitForCompletion(response.reference);
console.log('Signing completed!');

Example 5: Download Signed PDF

// Browser - Download as file
async function downloadSignedPdf(reference, filename) {
    const blob = await client.downloadSignedDocumentBlob(reference);
    ESignUtils.downloadBlob(blob, filename);
}

// Browser - Open in new tab
async function viewSignedPdf(reference) {
    const blob = await client.downloadSignedDocumentBlob(reference);
    ESignUtils.openPdfInNewTab(blob);
}

// Node.js - Save to file
const fs = require('fs');

async function saveSignedPdf(reference, filepath) {
    const buffer = await client.downloadSignedDocument(reference);
    fs.writeFileSync(filepath, Buffer.from(buffer));
}

Example 6: Complete Browser Implementation

<!DOCTYPE html>
<html>
<head>
    <title>eSign Document Signing</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input, select { width: 100%; padding: 8px; }
        button { padding: 10px 20px; background: #4f46e5; color: white; border: none; cursor: pointer; }
        button:hover { background: #4338ca; }
        .result { margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 4px; }
    </style>
</head>
<body>
    <h1>Document Signing</h1>

    <div class="form-group">
        <label>Select PDF File</label>
        <input type="file" id="pdfFile" accept=".pdf">
    </div>

    <div class="form-group">
        <label>Signer Name</label>
        <input type="text" id="signerName" placeholder="Enter your name">
    </div>

    <div class="form-group">
        <label>Authentication Mode</label>
        <select id="authMode">
            <option value="online-aadhaar-otp">OTP</option>
            <option value="online-aadhaar-bio">Biometric</option>
            <option value="capricorn-ekyc-account">eKYC</option>
        </select>
    </div>

    <button onclick="startSigning()">Sign Document</button>
    <button onclick="downloadSigned()" id="downloadBtn" style="display:none; margin-left:10px;">
        Download Signed PDF
    </button>

    <div id="result" class="result" style="display:none;"></div>

    <script src="esign-sdk.min.js"></script>
    <script>
    const { ESignClient, ESignRequest, SigningOptions, ESignMode, ESignUtils } = ESignSDK;

    // Initialize client
    const client = new ESignClient({
        apiBaseUrl: 'https://your-api-server.com',
        token: 'your-token',
        key: 'your-key'
    });

    let currentReference = null;

    async function startSigning() {
        try {
            const file = document.getElementById('pdfFile').files[0];
            const signerName = document.getElementById('signerName').value;
            const mode = document.getElementById('authMode').value;

            if (!file || !signerName) {
                alert('Please select a file and enter your name');
                return;
            }

            // Convert file to Base64
            const pdf64 = await ESignUtils.fileToBase64(file);

            // Create options
            const options = new SigningOptions()
                .coordinates(350, 50, 550, 120)
                .allPages()
                .reason('Digital Signature')
                .location('India')
                .showGreenTick();

            // Create request
            const request = new ESignRequest()
                .pdf64(pdf64)
                .title(file.name)
                .mode(mode)
                .signerName(signerName)
                .options(options);

            // Sign document
            const response = await client.signDocument(request);

            // Store reference
            currentReference = response.reference;
            localStorage.setItem('esign_reference', response.reference);

            // Show result
            showResult(`
                <strong>Success!</strong><br>
                Reference: ${response.reference}<br>
                <a href="${response.redirectUrl}" target="_blank">Click here to complete OTP</a>
            `);

            // Show download button
            document.getElementById('downloadBtn').style.display = 'inline-block';

            // Open authentication page
            window.open(response.redirectUrl, '_blank');

        } catch (error) {
            showResult(`<strong>Error:</strong> ${error.message}`, true);
        }
    }

    async function downloadSigned() {
        try {
            const reference = currentReference || localStorage.getItem('esign_reference');

            if (!reference) {
                alert('No reference found. Please sign a document first.');
                return;
            }

            // Check status first
            const status = await client.getStatus(reference);

            if (!status.isCompleted()) {
                alert(`Signing not complete. Status: ${status.status}`);
                return;
            }

            // Download as blob
            const blob = await client.downloadSignedDocumentBlob(reference);

            // Save file
            ESignUtils.downloadBlob(blob, 'signed_document.pdf');

            showResult('<strong>Downloaded!</strong> Check your downloads folder.');

        } catch (error) {
            showResult(`<strong>Error:</strong> ${error.message}`, true);
        }
    }

    function showResult(html, isError = false) {
        const resultDiv = document.getElementById('result');
        resultDiv.style.display = 'block';
        resultDiv.style.background = isError ? '#fee2e2' : '#dcfce7';
        resultDiv.innerHTML = html;
    }
    </script>
</body>
</html>

Error Handling

ESignError

try {
    const response = await client.signDocument(request);
} catch (error) {
    if (error instanceof ESignError) {
        console.error('Code:', error.code);
        console.error('Message:', error.message);
    }
}

Error Codes

Code Description Solution
AUTH_001 Invalid token Check API token
AUTH_002 Invalid key Check API key
VAL_001 Missing required field Check request fields
VAL_002 Invalid PDF data Verify Base64 encoding
VAL_003 Invalid mode Use valid ESignMode
VAL_004 ekycId required Add ekycId for eKYC mode
ESP_001 User cancelled User cancelled signing
ESP_002 OTP verification failed Invalid OTP entered
ESP_003 Authentication failed Aadhaar auth failed
TIMEOUT Request timeout Increase timeout or retry
NETWORK Network error Check connectivity

Node.js Usage

Complete Node.js Example

const fs = require('fs');
const path = require('path');
const { ESignClient, ESignRequest, SigningOptions, ESignMode } = require('./esign-sdk.min.js');

// Initialize client
const client = new ESignClient({
    apiBaseUrl: 'https://your-api-server.com',
    token: 'your-token',
    key: 'your-key',
    timeout: 120000  // 2 minutes
});

async function signDocument() {
    // Read PDF file
    const pdfPath = path.join(__dirname, 'document.pdf');
    const pdfBuffer = fs.readFileSync(pdfPath);
    const pdf64 = pdfBuffer.toString('base64');

    // Create options
    const options = new SigningOptions()
        .coordinates(350, 50, 550, 120)
        .allPages()
        .reason('Server-side signing')
        .location('India');

    // Create request
    const request = new ESignRequest()
        .pdf64(pdf64)
        .title('Server Document')
        .mode(ESignMode.OTP)
        .signerName('John Doe')
        .callbackUrl('https://your-server.com/webhook/esign')
        .options(options);

    // Sign
    const response = await client.signDocument(request);

    console.log('Reference:', response.reference);
    console.log('Redirect URL:', response.redirectUrl);

    // Send redirectUrl to frontend for user authentication
    return response;
}

async function downloadAndSave(reference) {
    const buffer = await client.downloadSignedDocument(reference);

    const outputPath = path.join(__dirname, `signed_${reference}.pdf`);
    fs.writeFileSync(outputPath, Buffer.from(buffer));

    console.log('Saved to:', outputPath);
}

// Run
signDocument()
    .then(response => {
        console.log('Signing initiated');
        // After user completes OTP:
        // downloadAndSave(response.reference);
    })
    .catch(console.error);

Best Practices

1. Store Reference ID

Always store the reference ID after signing initiation:

// Browser
localStorage.setItem('esign_ref', response.reference);

// Server
await database.save({ reference: response.reference, userId: user.id });

2. Handle Errors Gracefully

try {
    const response = await client.signDocument(request);
} catch (error) {
    if (error.code === 'TIMEOUT') {
        // Retry with longer timeout
        client.timeout = 120000;
        return client.signDocument(request);
    }
    throw error;
}

3. Use Callback URL for Server Notifications

const request = new ESignRequest()
    .pdf64(pdf64)
    .title('Document')
    .mode(ESignMode.OTP)
    .signerName('User')
    .callbackUrl('https://your-server.com/webhook/esign');  // Webhook

4. Validate Before Building

// The build() method validates required fields
try {
    const request = new ESignRequest()
        .pdf64(pdf64)
        .title('Document')
        // Missing mode and signerName!
        .build();
} catch (error) {
    console.error('Validation failed:', error.message);
}

Troubleshooting

Problem Cause Solution
CORS error API server not configured Enable CORS on server
Network timeout Large PDF or slow network Increase timeout, use pdfUrl
Invalid PDF error Corrupted Base64 Re-encode PDF properly
OTP page blank WebView/popup blocked Allow popups for domain
Download fails Signing not complete Check status first

FAQ

Can I sign multiple documents at once?

No, each signing request handles one document. For bulk signing, loop through documents sequentially.

How long is the redirect URL valid?

Typically 15-30 minutes. After that, create a new signing request.

Can I use the SDK without a backend server?

No. The SDK is a client library that communicates with your eSign API server.

What PDF sizes are supported?

Up to 10MB for Base64. For larger files, use pdfUrl() to have the server fetch the PDF.


Version History

Version Date Changes
1.0.0 December 2025 Initial release

© 2025 Capricorn Technologies. All rights reserved.