npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@docuplete/sdk

v0.1.0

Published

Official TypeScript/JavaScript SDK for the Docuplete API

Readme

@docuplete/sdk

Official TypeScript/JavaScript SDK for the Docuplete API.

Installation

npm install @docuplete/sdk
# or
pnpm add @docuplete/sdk

Requires Node.js 18 or later (uses native fetch and crypto).


Quick start

import { Docuplete } from "@docuplete/sdk";

const client = new Docuplete({ apiKey: process.env.DOCUPLETE_API_KEY! });

// 1. Pick an active package
const packages = await client.packages.list();
const pkg = packages.find(p => p.active)!;

// 2. Create a session — returns a ready-to-use interview URL
const { sessionToken, interviewUrl, expiresAt } = await client.sessions.create({
  packageId: pkg.id,
  prefill: {
    firstName: "Jane",
    lastName:  "Smith",
    email:     "[email protected]",
  },
  linkExpiryDays: 7,
});

// Send the client to their interview
console.log("Interview URL:", interviewUrl);
console.log("Expires at:", expiresAt ?? "never");

// 3. Poll or use webhooks to detect completion
const session = await client.sessions.get(sessionToken);
if (session.status === "generated") {
  console.log("Answers:", session.answers);
}

Authentication

All SDK routes use a single API key format: dp_live_…

Generate your keys in the Docuplete dashboard under Settings → Developer.

const client = new Docuplete({ apiKey: process.env.DOCUPLETE_API_KEY! });

Store keys in environment variables — never hard-code them or expose them client-side.

Options

const client = new Docuplete({
  apiKey:  process.env.DOCUPLETE_API_KEY!,
  baseUrl: "https://staging.docuplete.app",                 // optional — override for testing/staging
  timeout: 15_000,  // optional — ms, default 30 000
});

Try it without an API key

Use the sandbox to explore the interview flow without any credentials:

const { interviewUrl } = await client.sandbox.start({
  firstName: "Jane",
  email:     "[email protected]",
});
// Open interviewUrl in a browser — no sign-up required

The sandbox endpoint is publicly accessible and requires no authentication.


Resources

client.sessions

sessions.create(params)dp_live_… key

Creates a new interview session and returns a ready-to-use interview URL. Prefill values use field source keys as keys (found in the field editor).

const { sessionToken, interviewUrl, expiresAt } = await client.sessions.create({
  packageId: 42,
  prefill: {
    firstName:     "Jane",
    lastName:      "Smith",
    email:         "[email protected]",
    accountNumber: "ACC-12345",
  },
  linkExpiryDays: 7,    // optional — null for no expiry
  locale:         "en", // optional — defaults to org setting
});

sessions.get(token)

Fetches the current state of a session including answers and status.

const session = await client.sessions.get("df_abc123");
// session.status:  "draft" | "in_progress" | "generated" | "voided"
// session.answers: Record<string, unknown>  — filled once submitted

sessions.list(params?)

Lists sessions with optional filters, most-recently updated first.

const { sessions, total } = await client.sessions.list({
  packageId:    42,
  status:       "generated",
  limit:        25,
  offset:       0,
  updatedAfter: "2026-01-01T00:00:00Z", // incremental sync
});

sessions.void(token, params?)

Immediately invalidates the interview link. Returns 409 if the session is already voided or generated. Cannot be undone.

const result = await client.sessions.void("df_abc123", {
  reason:       "Client withdrew",  // optional — stored in audit log
  notifySigner: true,               // optional — email the signer
});
// result.ok, result.token, result.voidedAt

sessions.sendLink(token, params)

Sends (or re-sends) the interview link email from your org's address.

await client.sessions.sendLink("df_abc123", {
  recipientEmail: "[email protected]",
  recipientName:  "Jane Smith",
  customMessage:  "Please complete at your earliest convenience.",
});

sessions.updateAnswers(token, answers)

Saves answers programmatically — use this when filling the form server-side instead of sending the client to the interview URL.

const session = await client.sessions.updateAnswers("df_abc123", {
  firstName:  "Jane",
  lastName:   "Smith",
  iraAmount:  "50000",
});

sessions.generate(token)

Triggers final PDF generation and fires integrations (Google Drive, HubSpot, webhooks).

const result = await client.sessions.generate("df_abc123");

if (result.status === "generated") {
  // Synchronous path (Redis unavailable) — download immediately
  console.log("PDF ready:", result.downloadUrl);
} else {
  // Async path — poll until ready
  const { jobId } = result;
  let done = false;
  while (!done) {
    await new Promise(r => setTimeout(r, 2000));
    const s = await client.sessions.getGenerateStatus("df_abc123", jobId);
    if (s.status === "ready")  { console.log(s.downloadUrl); done = true; }
    if (s.status === "failed") throw new Error(s.error ?? "Generation failed");
  }
}

sessions.getGenerateStatus(token, jobId?)

Polls the status of a background generation job.

const status = await client.sessions.getGenerateStatus(token, jobId);
// status.status:      "pending" | "processing" | "ready" | "failed"
// status.downloadUrl: string | undefined  — present when "ready"
// status.error:       string | undefined  — present when "failed"

client.packages

packages.list()

Returns all packages for your account.

const packages = await client.packages.list();
const active = packages.filter(p => p.active);

packages.get(id)

Returns a single package by its numeric ID.

const pkg = await client.packages.get(42);

packages.webhookDeliveries(id, params?)dp_live_… key

Returns the webhook delivery log for a package (most recent first).

const { deliveries, total } = await client.packages.webhookDeliveries(42, {
  limit:  50,
  offset: 0,
});

client.account

account.get()

Returns the account and role associated with the API key.

const account = await client.account.get();
// { accountId, accountName, slug, email, role }

client.sandbox

sandbox.start(params?) — no API key required

Starts a public demo interview session backed by a seeded demo package. Sandbox tokens are prefixed df_sbx_ and expire after 7 days.

const { sessionToken, interviewUrl, prefill, expiresAt } = await client.sandbox.start({
  firstName: "Jane",
  lastName:  "Smith",
  email:     "[email protected]",
  // dateOfBirth, addressLine1, city, state, zip
});

Webhooks

Docuplete sends signed events to your webhook URL at key points in the session lifecycle. The primary event is pdf.generated, fired when the PDF packet is ready to download. Always verify the signature before processing the payload.

Verifying signatures

Use verifyWebhookSignature (returns boolean) or constructWebhookEvent (throws on failure):

import express from "express";
import { constructWebhookEvent } from "@docuplete/sdk";

app.post(
  "/webhook",
  express.raw({ type: "application/json" }), // must receive raw bytes
  async (req, res) => {
    const sig    = req.headers["x-docuplete-signature"] as string;
    const secret = process.env.DOCUPLETE_WEBHOOK_SECRET!;

    let event;
    try {
      event = await constructWebhookEvent(req.body.toString(), sig, secret);
    } catch {
      return res.status(401).send("Invalid signature");
    }

    if (event.event === "pdf.generated") {
      console.log("Session token:", event.sessionToken);
      console.log("Download URL:", event.downloadUrl);
      console.log("Answers:", event.answers);
    }

    res.sendStatus(200);
  },
);

The signature header contains sha256=<hex-encoded-hmac>. Retrieve your webhook secret from Settings → Webhooks or via:

curl -H "Authorization: Bearer dp_live_..." \
     https://api.docuplete.com/api/v1/packages/42/webhook-secret

WebhookPayload shape

The payload is a discriminated union — use the event field to narrow the type:

// Fired when the PDF packet is produced and ready to download
interface PdfGeneratedPayload {
  event:        "pdf.generated";
  packageId:    number;
  packageName:  string;
  sessionToken: string;
  generatedAt:  string;                  // ISO 8601
  prefill:      Record<string, unknown>;
  answers:      Record<string, unknown>;
  downloadUrl:  string | null;           // signed URL, valid for 24 hours
}

// Fired when the recipient submits the interview form
interface SessionSubmittedPayload {
  event:        "session.submitted";
  packageId:    number;
  packageName:  string;
  sessionToken: string;
  submittedAt:  string;                  // ISO 8601
  prefill:      Record<string, unknown>;
  answers:      Record<string, unknown>;
}

// Full union type (all events) is exported from @docuplete/sdk as WebhookPayload

Programmatic flow (fully server-side)

Use this pattern when you want to fill and generate a packet without the client completing a form:

const client = new Docuplete({ apiKey: process.env.DOCUPLETE_API_KEY! });

// Create session
const { sessionToken } = await client.sessions.create({ packageId: 42 });

// Fill answers using field source keys
await client.sessions.updateAnswers(sessionToken, {
  firstName:     "Jane",
  lastName:      "Smith",
  accountNumber: "ACC-12345",
  iraAmount:     "50000",
});

// Generate the PDF
const result = await client.sessions.generate(sessionToken);
if (result.status === "generated") {
  console.log("PDF ready:", result.downloadUrl);
}

Error handling

All methods throw DocupleteError on non-2xx responses.

import { Docuplete, DocupleteError } from "@docuplete/sdk";

try {
  const pkg = await client.packages.get(999);
} catch (err) {
  if (err instanceof DocupleteError) {
    console.error(`API error ${err.status}: ${err.message}`);
    // err.status  — HTTP status code (404, 401, 400, …)
    // err.code    — machine-readable code (e.g. "api_error", "network_error")
    // err.issues  — validation issue strings, present on 400 responses
  }
}

TypeScript

All types are exported from the main entry point:

import type {
  Package,
  Session,
  SessionListItem,
  SessionStatus,
  Account,
  CreateSessionParams,
  CreateSessionResult,
  GenerateSessionResult,
  GenerateStatusResult,
  SandboxStartParams,
  SandboxStartResult,
  WebhookDelivery,
  WebhookPayload,
  SupportedLocale,
} from "@docuplete/sdk";

License

MIT