pandadoc-signing
v0.0.0-a.2
Published
The `pandadoc-signing` can be used to embed the PandaDoc Document Signing experience in your web application.
Readme
pandadoc-signing Library
The pandadoc-signing library provides an easy way to embed the PandaDoc Document Signing experience in your web application using Session id.
Features
- Embedded Document Signing
- Session ID authentication (simplified setup)
- Event handling with type-safe callbacks
- Debug mode for troubleshooting
- Region support (COM/EU)
- Lightweight and easy to integrate
Installation
To install pandadoc-signing, you can use either npm or yarn:
npm install pandadoc-signingor
yarn add pandadoc-signingQuick Start
First, import the library in your project:
import { Signing } from "pandadoc-signing";Then, initialize and open the signing experience:
const signing = new Signing(
"signing-container",
{},
{
region: "com", // Optional: specify region ('com' or 'eu')
}
);
// Open the signing experience
await signing.open({
sessionId: "your-session-id-here", // Session ID for authentication
});API Reference
Constructor
const signing = new Signing(elementId, config, pdConfig);Parameters:
elementId(string): The ID of the DOM element to replace with the signing iframeconfig(SigningConfig): Signing configuration optionspdConfig(PDConfigModel): PandaDoc server configuration
Note: If sessionId is provided in the configuration, the signing experience will automatically open once the iframe is ready and initialized.
Config Options:
interface SigningConfig {
width?: number | string; // Width of the iframe
height?: number | string; // Height of the iframe
sessionId?: string; // Session ID for authentication
debugMode?: boolean; // Enable debug logging
}PDConfig Options:
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
},
{
region: "eu", // Preferred: Use region for better maintainability
host: "https://custom.pandadoc.com/", // Deprecated: Use region instead
}
);Methods
open(options)
Opens the signing experience with the specified session ID.
await signing.open({
sessionId: "your-session-id-here",
});
// Or, if sessionId was provided in the constructor config:
await signing.open();Parameters:
options(optional): PartialSigningConfigobjectsessionId(string, optional): Session ID for authentication. If not provided, uses thesessionIdfrom the constructor configuration.
Returns: Promise
Behavior:
- If no
sessionIdis provided (neither in options nor in config), the method returns early without opening. - If the signing instance is not yet initialized (iframe not ready), the method returns early.
- The method will use the
sessionIdfrom the constructor config if not provided in the options.
close()
Closes the signing experience by dispatching a close command to the iframe.
await signing.close();Returns: Promise
Behavior:
- If the signing experience is not in a ready state, the method returns early without dispatching the close command.
- When successful, the signing status is set to
CLOSED.
destroy()
Destroys the signing instance and cleans up resources to prevent memory leaks. This method:
- Destroys the event bus and command bus
- Removes the iframe from the DOM
- Should be called when you no longer need the signing instance
signing.destroy();Important: After calling destroy(), the signing instance cannot be reused. Create a new instance if needed.
on(event, handler)
Registers an event handler for signing session events.
signing.on("document.loaded", (payload) => {
console.log("Document loaded:", payload);
});
signing.on("document.completed", (payload) => {
console.log("Document completed:", payload);
});
signing.on("document.exception", (payload) => {
console.error("Document exception:", payload);
});Parameters:
event(string): Event name. Available events:'document.loaded': Triggered when the document is loaded in the signing session'document.completed': Triggered when a recipient completes the document'document.exception': Triggered if an error occurs during document finalization
handler(function): Callback function that receives the event payload
Returns: The Signing instance (for method chaining)
TypeScript:
signing.on("document.loaded", (payload: DocumentEventPayload) => {
// payload is fully typed
});
signing.on("document.completed", (payload: DocumentEventPayload) => {
// payload is fully typed
});
signing.on("document.exception", (payload: DocumentExceptionPayload) => {
// payload is fully typed
});off(event, handler?)
Removes an event handler. If no handler is specified, removes all handlers for the event.
// Remove specific handler
const handler = (payload) => console.log(payload);
signing.on("document.completed", handler);
signing.off("document.completed", handler);
// Remove all handlers for an event
signing.off("document.completed");Parameters:
event(string): Event namehandler(function, optional): Specific handler to remove
Returns: The Signing instance (for method chaining)
once(event, handler)
Registers an event handler that will be called only once.
signing.once("document.completed", (payload) => {
console.log("Document completed (one-time handler):", payload);
});Parameters:
event(string): Event namehandler(function): Callback function
Returns: The Signing instance (for method chaining)
Event Handling
The library supports event handling through an EventEmitter-style API using .on(), .off(), and .once() methods.
Available Events
'document.loaded': Triggered when the document is loaded in the signing session.'document.completed': Triggered when a recipient completes the document.'document.exception': Triggered if an error occurs during signing.
Basic Usage Example
import { Signing } from "pandadoc-signing";
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
},
{
region: "com",
}
);
// Register event handlers
signing.on("document.loaded", (payload) => {
console.log("Document loaded:", payload);
// Hide loading spinner, show signing UI, etc.
});
signing.on("document.completed", (payload) => {
console.log("Document completed:", payload);
// Redirect user, update UI, notify backend, etc.
window.location.href = "/thank-you";
});
signing.on("document.exception", (payload) => {
console.error("Document exception:", payload);
// Show error message, log error, etc.
});
await signing.open();Advanced Usage
Multiple Handlers for Same Event
// You can register multiple handlers for the same event
signing.on("document.completed", (payload) => {
updateUI(payload);
});
signing.on("document.completed", (payload) => {
notifyBackend(payload);
});
signing.on("document.completed", (payload) => {
trackAnalytics(payload);
});One-Time Handlers
// Handler will be called only once, then automatically removed
signing.once("document.completed", (payload) => {
console.log("Document completed:", payload);
});Removing Handlers
const handler = (payload) => {
console.log("Document completed:", payload);
};
// Register handler
signing.on("document.completed", handler);
// Later, remove specific handler
signing.off("document.completed", handler);
// Or remove all handlers for an event
signing.off("document.completed");Method Chaining
signing
.on("document.loaded", handleLoaded)
.on("document.completed", handleCompleted)
.on("document.exception", handleException);Debug Mode
Enable debug mode to get detailed logging of signing operations:
const signing = new Signing("signing-container", {
sessionId: "your-session-id",
debugMode: true,
});When debug mode is enabled, you'll see console logs for:
- Constructor initialization
- Open/close operations with parameters
- Command dispatching
- iframe creation
- Event subscription
- Error conditions and their reasons
- Resource cleanup during destroy
Debug logs are prefixed with [Signing Debug] and include relevant data for troubleshooting.
Region Configuration
You can specify which PandaDoc region to use by setting the region parameter:
// For global region (default)
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
},
{
region: "com", // Global region - app.pandadoc.com
}
);
// For EU region
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
},
{
region: "eu", // EU region - app.pandadoc.eu
}
);Available Regions
'com': Global region (app.pandadoc.com) - Default'eu': EU region (app.pandadoc.eu)
Complete Usage Example
import { Signing } from "pandadoc-signing";
// Initialize the signing experience
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
debugMode: true, // Enable for development
width: 800,
height: 600,
},
{
region: "com", // Use global region (default)
}
);
try {
// Open the signing experience
await signing.open();
// ... user signs the document ...
// Close when done (optional - usually handled by the signing flow)
await signing.close();
} finally {
// Always clean up resources
signing.destroy();
}HTML Integration Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document Signing</title>
<style>
body,
html {
margin: 0;
padding: 0;
overflow: hidden;
}
iframe {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="signing-container"></div>
<script type="module">
import { Signing } from "pandadoc-signing";
const signing = new Signing(
"signing-container",
{
sessionId: "your-session-id-here",
debugMode: true,
},
{
region: "com",
}
);
await signing.open();
</script>
</body>
</html>Getting an Session ID
To use this library, you need to obtain an Session ID from the PandaDoc API:
- Call the Create Document Signing Session API endpoint
- Extract the
idsession ID from the response - Use the session ID with the
pandadoc-signinglibrary
Example API call:
const response = await fetch(
"https://api.pandadoc.com/public/v1/documents/{document_id}/session",
{
method: "POST",
headers: {
Authorization: "API-Key your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
recipient: "[email protected]",
lifetime: 3600, // Session lifetime in seconds
}),
}
);
const data = await response.json();
const sessionId = data.id; // This is your session IDPerformance Optimization
Pre-initialize for Instant Loading
For the best user experience, you can initialize the Signing widget before requesting the session ID from the API. This allows the iframe and all static resources to load in advance, so when the session ID is ready, the document opens almost instantly.
Recommended Pattern:
import { Signing } from "pandadoc-signing";
// Step 1: Initialize the signing widget immediately (without session ID)
const signing = new Signing(
"signing-container",
{
debugMode: true,
},
{
region: "com",
}
);
// Step 2: Request session ID from your backend in parallel
const sessionPromise = fetch("/api/create-signing-session", {
method: "POST",
body: JSON.stringify({ documentId: "your-document-id" }),
})
.then((response) => response.json())
.then((data) => data.sessionId);
// Step 3: Once session ID is ready, open immediately (resources already loaded!)
const sessionId = await sessionPromise;
await signing.open({ sessionId });
// The document will open almost instantly because the iframe is already initializedBenefits:
- ✅ Static resources (iframe, scripts, styles) load in parallel with your API call
- ✅ Reduces perceived loading time significantly
- ✅ Document opens quickly when session ID is available
- ✅ Better user experience, especially on slower connections
Without Pre-initialization (Slower):
// ❌ Not recommended: Initialize only after getting session ID
const sessionId = await getSessionIdFromAPI();
const signing = new Signing("signing-container"); // Resources start loading NOW
await signing.open({ sessionId }); // User waits for iframe + resources + documentWith Pre-initialization (Faster):
// ✅ Recommended: Initialize while fetching session ID
const signing = new Signing("signing-container"); // Resources start loading NOW
const sessionId = await getSessionIdFromAPI(); // Parallel execution
await signing.open({ sessionId }); // Only document loading time, resources ready!Common Use Cases
1. Basic Signing Flow
const signing = new Signing("signing-container", {
sessionId: "session-id-from-api",
});
await signing.open();2. Custom Size
const signing = new Signing("signing-container", {
sessionId: "session-id-from-api",
width: 1024,
height: 768,
});
await signing.open();3. EU Region
const signing = new Signing(
"signing-container",
{
sessionId: "session-id-from-api",
},
{
region: "eu",
}
);
await signing.open();4. Development with Debug Mode
const signing = new Signing("signing-container", {
sessionId: "session-id-from-api",
debugMode: true,
});
await signing.open();
// Check console for detailed logs5. Event Handling
const signing = new Signing("signing-container", {
sessionId: "session-id-from-api",
});
signing.on("document.loaded", (payload) => {
// Document is ready for signing
hideLoadingSpinner();
});
signing.on("document.completed", (payload) => {
// Handle successful completion
showSuccessMessage();
redirectToThankYouPage();
});
signing.on("document.exception", (payload) => {
// Handle errors
showErrorMessage(payload || "An error occurred");
});
await signing.open();Browser Support
The library supports all modern browsers that support:
- ES2015+
- iframe embedding
- Promises/async-await
Building
To build the library, run:
vite buildThis will generate the library bundle in the dist directory.
TypeScript Support
The library includes TypeScript definitions out of the box. Import types as needed:
import {
Signing,
SigningConfig,
PDConfigModel,
Region,
DocumentEventPayload,
DocumentExceptionPayload,
} from "pandadoc-signing";
const config: SigningConfig = {
sessionId: "your-session-id-here",
debugMode: true,
width: 800,
height: 600,
};
const pdConfig: PDConfigModel = {
region: Region.COM,
};
const signing = new Signing("signing-container", config, pdConfig);
// Event handlers with full type safety
signing.on("document.loaded", (payload: DocumentEventPayload) => {
console.log("Document loaded");
});
signing.on("document.completed", (payload: DocumentEventPayload) => {
console.log("Document completed:", payload.status);
});
signing.on("document.exception", (payload: DocumentExceptionPayload) => {
console.error("Error:", payload.error);
});