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

@chaindoc_io/server-sdk

v2.1.0

Published

Server-side SDK for Chaindoc API - document management, signatures, and embedded sessions

Readme

@chaindoc_io/server-sdk

Official server-side SDK for Chaindoc API - documents, signatures, contracts, invoicing, and webhook-safe integrations.

npm version License: MIT TypeScript Node.js

Features

  • Document Management - Create, update, and version documents with blockchain verification
  • Digital Signatures - Request and collect legally-binding electronic signatures
  • Templates, Contracts, and Billing - Generate documents and contracts from published templates, then manage invoice lifecycle and transactions
  • Embedded Signing - Seamless in-app signing experience with frontend SDK integration
  • Blockchain Verification - Immutable document verification on blockchain
  • Webhook Verification - Verify signed webhook deliveries with replay protection
  • Embedded KYC Enforcement - Require Chaindoc-managed KYC inside the signing iframe
  • Zero Dependencies - Uses native Node.js 18+ APIs (fetch, FormData)
  • TypeScript First - Full type definitions included
  • Automatic Retries - Built-in retry logic with exponential backoff

Installation

npm install @chaindoc_io/server-sdk
yarn add @chaindoc_io/server-sdk
pnpm add @chaindoc_io/server-sdk

Quick Start

import { Chaindoc } from "@chaindoc_io/server-sdk";

const chaindoc = new Chaindoc({
  secretKey: "sk_your_secret_key",
});

// Create embedded session for document signing
const session = await chaindoc.embedded.createSession({
  email: "[email protected]",
  metadata: {
    documentId: "doc_xxx",
    signatureRequestId: "req_xxx",
  },
});

// Use session.sessionId on frontend with @chaindoc_io/embed-sdk
console.log(session.sessionId);

Complete Example: Document Signing Flow

import { Chaindoc } from "@chaindoc_io/server-sdk";
import { readFile } from "fs/promises";

const chaindoc = new Chaindoc({
  secretKey: "sk_your_secret_key",
});

// 1. Upload document
const buffer = await readFile("./contract.pdf");
const file = new Blob([buffer], { type: "application/pdf" });
const { media } = await chaindoc.media.upload([file]);

// 2. Create document with blockchain verification
const doc = await chaindoc.documents.create({
  name: "Service Agreement",
  description: "Contract for services",
  media: media[0],
  hashtags: ["#contract", "#agreement"],
  status: "published", // Verify in blockchain
});

// 3. Create signature request
const request = await chaindoc.signatures.createRequest({
  versionId: doc.document.currentVersion.id,
  recipients: [{ email: "[email protected]" }],
  deadline: new Date("2027-12-31"),
  embeddedFlow: true,
  isKycRequired: true,
});

// 4. Create embedded session for signer
const session = await chaindoc.embedded.createSession({
  email: "[email protected]",
  metadata: {
    documentId: doc.documentId,
    signatureRequestId: request.requestId,
    returnUrl: "https://yourapp.com/signing-complete",
  },
});

// 5. Send sessionId to frontend
// Frontend uses: @chaindoc_io/embed-sdk
// sdk.openSignatureFlow({ sessionId: session.sessionId })

Complete Example: Contract Billing Flow

import { Chaindoc } from "@chaindoc_io/server-sdk";

const chaindoc = new Chaindoc({
  secretKey: "sk_your_secret_key",
});

// Contract should already be created, sent, and signed by both parties
const contract = await chaindoc.contracts.get("contract_uuid");
const contractId = contract.contractId;

if (contract.contract.status !== "active") {
  throw new Error("Invoices can be created only for active contracts");
}

const invoice = await chaindoc.invoices.create(contractId, {
  title: "April Retainer",
  amount: "1500.00",
  dueDate: "2026-04-30T00:00:00.000Z",
  sendImmediately: false,
});

const invoiceId = invoice.invoiceId ?? invoice.invoice.id;

await chaindoc.invoices.send(contractId, invoiceId, {
  autoCharge: false,
});

const refreshedInvoice = await chaindoc.invoices.get(contractId, invoiceId);

if (refreshedInvoice.invoice.status === "unpaid") {
  await chaindoc.invoices.charge(contractId, invoiceId);
}

const transactions = await chaindoc.transactions.listByContract(contractId);

console.log(transactions.transactions.map((transaction) => transaction.id));

Complete Example: Template Runtime Flow

import { Chaindoc } from "@chaindoc_io/server-sdk";

const chaindoc = new Chaindoc({
  secretKey: "sk_your_secret_key",
});

const templateId = "template_uuid";

const draftDocument = await chaindoc.templates.createDocument(templateId, {
  documentName: "Generated NDA",
  documentDescription: "Rendered from a published Chaindoc template",
  variables: {
    client_name: "Acme Inc.",
    effective_date: "2026-04-10",
  },
});

const signatureRequest = await chaindoc.templates.sendForSigning(templateId, {
  documentName: "Generated NDA",
  documentDescription: "Ready for signing",
  variables: {
    client_name: "Acme Inc.",
    effective_date: "2026-04-10",
  },
  slotAssignments: [{ signerKey: "party_a", email: "[email protected]" }],
  deadline: new Date("2026-12-31"),
});

const contract = await chaindoc.templates.createContract(templateId, {
  title: "Generated Master Services Agreement",
  description: "Contract rendered from template",
  variables: {
    company_name: "Acme Inc.",
    effective_date: "2026-04-10",
  },
  contragent: {
    email: "[email protected]",
    name: "Partner Corp",
  },
  slotAssignments: [
    { signerKey: "party_a", role: "business" },
    { signerKey: "party_b", role: "contragent" },
  ],
  deadline: new Date("2026-12-31"),
});

console.log(draftDocument.documentId);
console.log(signatureRequest.requestId);
console.log(contract.contractId);

Configuration

const chaindoc = new Chaindoc({
  secretKey: "sk_xxx", // Required - Your secret API key
  environment: "production", // Optional: 'production' | 'staging' | 'development'
  timeout: 30000, // Optional: Request timeout in ms (default: 30000)
  retry: {
    // Optional: Retry configuration
    maxRetries: 3,
    initialDelay: 1000,
    maxDelay: 10000,
  },
});

Environments

| Environment | API URL | Use Case | | ------------- | ----------------------------------- | ----------------------- | | production | https://api.chaindoc.io (default) | Live production traffic | | staging | https://api-demo.chaindoc.io | Pre-release testing | | development | https://api-demo.chaindoc.io | Development & debugging |

Email handling

The SDK trims and lowercases every email value before sending it to the API. This applies to top-level fields (email, signerEmail) and to nested email fields inside recipients, fields, slotAssignments, accessEmails, and contragent. Your input objects are not mutated — the SDK copies them shallowly before normalizing. This matches the backend's canonical email format and prevents silent auto-link misses for users who registered with a different case.

API Overview

Documents

// Create document
await chaindoc.documents.create({
  name: string;
  description: string;
  media: Media;
  hashtags: string[];
  status: 'draft' | 'published';
  accessType?: 'private' | 'public' | 'restricted';
});

// Update document (creates new version)
await chaindoc.documents.update(documentId, params);

// Update access rights
await chaindoc.documents.updateRights(documentId, {
  accessType: 'restricted',
  accessEmails: [{ email: '[email protected]', level: 'read' }],
});

// Verify document in blockchain
await chaindoc.documents.verify({ versionHash: '0x...' });

// Get verification status
await chaindoc.documents.getVerificationStatus(versionId);

Signatures

// Create signature request
await chaindoc.signatures.createRequest({
  versionId: string;
  recipients: [{ email: string }];
  deadline: Date;
  embeddedFlow?: boolean;
  isKycRequired?: boolean;
});

// Get request status
await chaindoc.signatures.getRequestStatus(requestId);

// Get all requests
await chaindoc.signatures.getMyRequests({ pageNumber: 1, pageSize: 10 });

// Sign document
await chaindoc.signatures.sign({ requestId, signatureId });

Embedded Sessions

// Create session for frontend signing (sends OTP to email)
const session = await chaindoc.embedded.createSession({
  email: '[email protected]',
  metadata: {
    documentId: string;
    signatureRequestId?: string;
    returnUrl?: string;
  },
});
// Returns sessionId for @chaindoc_io/embed-sdk

Contracts

const created = await chaindoc.contracts.create({
  documentId: "doc_uuid",
  title: "Master Services Agreement",
  contragent: { email: "[email protected]", name: "Partner LLC" },
  paymentMethodRequired: true,
  preferredPaymentMethodType: "card",
});

await chaindoc.contracts.list({ page: 1, limit: 10, status: "active" });
await chaindoc.contracts.get(created.contractId);
await chaindoc.contracts.getStatus(created.contractId);
await chaindoc.contracts.getActivities(created.contractId);
await chaindoc.contracts.send(created.contractId, {
  messageToSigners: "Please review and sign this contract",
  deadline: new Date("2026-12-31"),
  isKycRequired: true,
});
await chaindoc.contracts.cancel(created.contractId);
await chaindoc.contracts.terminate(created.contractId, { reason: "Mutual offboarding" });

Templates

await chaindoc.templates.createDocument(templateId, {
  documentName: "Generated NDA",
  variables: {
    client_name: "Acme Inc.",
  },
});

await chaindoc.templates.sendForSigning(templateId, {
  documentName: "Generated NDA",
  variables: {
    client_name: "Acme Inc.",
  },
  slotAssignments: [{ signerKey: "party_a", email: "[email protected]" }],
  deadline: new Date("2026-12-31"),
});

await chaindoc.templates.createContract(templateId, {
  title: "Generated MSA",
  variables: {
    company_name: "Acme Inc.",
  },
  contragent: {
    email: "[email protected]",
  },
  slotAssignments: [
    { signerKey: "party_a", role: "business" },
    { signerKey: "party_b", role: "contragent" },
  ],
  deadline: new Date("2026-12-31"),
});

Invoices

await chaindoc.invoices.create(contractId, {
  title: "April Retainer",
  amount: "1500.00",
  dueDate: "2026-04-30T00:00:00.000Z",
  autoCharge: false,
  sendImmediately: false,
});

await chaindoc.invoices.list(contractId, { status: "unpaid", page: 1, limit: 20 });
await chaindoc.invoices.get(contractId, invoiceId);
await chaindoc.invoices.send(contractId, invoiceId, { autoCharge: true });
await chaindoc.invoices.charge(contractId, invoiceId);
await chaindoc.invoices.markPaid(contractId, invoiceId, { note: "Wire received" });

Transactions

await chaindoc.transactions.listByContract(contractId);
await chaindoc.transactions.get(transactionId);

Media

// Upload files (PDF, DOC, images, videos)
const { media } = await chaindoc.media.upload([file1, file2]);

Utility

// Get API key info
await chaindoc.getApiKeyInfo();

// Health check
await chaindoc.healthCheck();

Webhooks

const verification = Chaindoc.webhooks.verify(
  rawBody,
  req.headers["x-chaindoc-signature"] as string,
  req.headers["x-chaindoc-timestamp"] as string,
  process.env.CHAINDOC_WEBHOOK_SECRET!
);

if (!verification.valid) {
  throw new Error("Invalid webhook signature");
}

switch (verification.envelope?.type) {
  case "contract.signed":
  case "invoice.created":
  case "invoice.sent":
  case "invoice.paid":
  case "transaction.created":
  case "transaction.updated":
    console.log(verification.envelope.data);
    break;
}

Error Handling

import { Chaindoc, ChaindocError } from '@chaindoc_io/server-sdk';

try {
  await chaindoc.documents.create({ ... });
} catch (error) {
  if (error instanceof ChaindocError) {
    console.error('API Error:', error.message);
    console.error('Status:', error.statusCode);
    console.error('Response:', error.response);
  }
}

Documentation

Requirements

Related Packages

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Security

For security vulnerabilities, please see our Security Policy.

License

MIT License - see LICENSE file for details.

Support


Made with ❤️ by the Chaindoc team