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

@railblocks/docusign-convex-component

v0.1.6

Published

A docusign integration component for Convex.

Readme

DocuSign Convex Component

npm version

A reusable Convex component for integrating DocuSign eSignature into your Convex applications.

Features

  • 🔐 OAuth 2.0 Authentication - Secure connection to DocuSign accounts
  • 📄 Envelope Management - Create, send, and track envelopes
  • 📋 Template Support - Create envelopes from templates with prefilled tabs
  • 🔔 Webhook Events - Automatically track all envelope status changes
  • 🔄 Token Refresh - Automatic access token refresh handling
  • 💾 Database Storage - Stores envelopes and events in Convex tables

Installation

npm install @railblocks/docusign-convex-component

Get Started

Add the component to your convex/convex.config.ts:

import { defineApp } from "convex/server";
import docusignIntegration from "@railblocks/docusign-convex-component/convex.config";

const app = defineApp();
app.use(docusignIntegration);

export default app;

Set environment variables in your Convex dashboard:

DOCUSIGN_INTEGRATION_KEY=your_integration_key
DOCUSIGN_SECRET_KEY=your_secret_key
ENCRYPTION_KEY=your_32_byte_encryption_key_in_hex  # Generate with: openssl rand -hex 32
DOCUSIGN_ENVIRONMENT=demo  # or "production"
DOCUSIGN_HMAC_KEY=your_webhook_hmac_key  # Optional but recommended

Create a DocuSign instance in convex/docusign.ts:

import { components } from "./_generated/api";
import { DocuSign } from "@railblocks/docusign-convex-component";

export const docusign = new DocuSign(components.docusignIntegration, {});

Register HTTP routes in convex/http.ts:

import { httpRouter } from "convex/server";
import { registerDocuSignRoutes } from "@railblocks/docusign-convex-component";
import { docusign } from "./docusign";

const http = httpRouter();
registerDocuSignRoutes(http, docusign);

export default http;

This registers:

  • GET /docusign/oauth/callback - OAuth callback
  • POST /docusign/webhooks - Webhook receiver

Quick Start

1. Set Up Connection

Option A: Using Existing Tokens (Recommended for Single-User Apps)

import { action } from "./_generated/server";
import { docusign } from "./docusign";

export const setupDocuSign = action({
  handler: async ctx => {
    return await docusign.setupWithTokens(
      ctx,
      "my-company-id",
      "YOUR_ACCESS_TOKEN",
      "YOUR_REFRESH_TOKEN",
    );
  },
});

Option B: OAuth Flow (For Multi-User Apps)

export const getAuthUrl = action({
  handler: async ctx => {
    const userId = "user_123"; // Your auth logic
    return await docusign.initiateOAuth(
      ctx,
      userId,
      "http://localhost:3000/docusign/oauth/callback",
    );
  },
});

2. Create and Send an Envelope

import { internalAction } from "./_generated/server";
import { docusign } from "./docusign";

export const sendDocument = internalAction({
  handler: async ctx => {
    return await docusign.createEnvelope(ctx, {
      userId: "user_123",
      emailSubject: "Please sign this document",
      recipients: [
        {
          email: "[email protected]",
          name: "John Doe",
          recipientId: "1",
        },
      ],
      documents: [
        {
          documentBase64: "...", // Your base64 encoded PDF
          name: "Contract.pdf",
          fileExtension: "pdf",
          documentId: "1",
        },
      ],
      status: "sent", // or "created" to send later
    });
  },
});

3. Create from Template

export const createFromTemplate = internalAction({
  handler: async ctx => {
    return await docusign.createFromTemplate(ctx, {
      userId: "user_123",
      templateId: "your-template-id",
      emailSubject: "Please sign",
      templateRoles: [
        {
          email: "[email protected]",
          name: "John Doe",
          roleName: "Signer",
          tabs: {
            textTabs: [
              { tabLabel: "name", value: "John Doe" },
              { tabLabel: "date", value: "2024-01-15" },
            ],
          },
        },
      ],
    });
  },
});

Query Envelopes

import { internalQuery } from "./_generated/server";
import { docusign } from "./docusign";

export const getEnvelope = internalQuery({
  handler: async (ctx, args: { userId: string; envelopeId: string }) => {
    return await docusign.getEnvelope(ctx, args.userId, args.envelopeId);
  },
});

export const listEnvelopes = internalQuery({
  handler: async (ctx, args: { userId: string }) => {
    return await docusign.listEnvelopes(ctx, args.userId);
  },
});

Advanced Usage

Configure Webhooks in DocuSign

  1. In DocuSign Settings → Connect → Add Configuration
  2. Set webhook URL: https://your-app.convex.site/docusign/webhooks
  3. Select events: Envelope Sent, Delivered, Completed, Declined, Voided

The component automatically stores events and updates envelope statuses.

Send an Envelope Created with status="created"

export const sendEnvelope = internalAction({
  handler: async (ctx, args: { userId: string; envelopeId: string }) => {
    return await docusign.sendEnvelope(ctx, args.userId, args.envelopeId);
  },
});

Void an Envelope

export const voidEnvelope = internalAction({
  handler: async (ctx, args: { userId: string; envelopeId: string }) => {
    return await docusign.voidEnvelope(ctx, args.userId, args.envelopeId, "No longer needed");
  },
});

Custom API Calls

Need to call a DocuSign endpoint not covered by the built-in methods?

export const listTemplates = action({
  handler: async ctx => {
    return await docusign.makeAPICall(ctx, "user_123", "/templates", "GET");
  },
});

See DocuSign REST API docs for all available endpoints.

DocuSign Component Options

const docusign = new DocuSign(components.docusignIntegration, {
  integrationKey: "...", // Optional, reads from env
  secretKey: "...", // Optional, reads from env
  encryptionKey: "...", // Required for security
  environment: "production", // "demo" or "production"
  hmacKey: "...", // For webhook validation
});

API Reference

OAuth

  • docusign.initiateOAuth(ctx, userId, redirectUri, state?) - Get authorization URL
  • docusign.setupWithTokens(ctx, userId, accessToken, refreshToken) - Use existing tokens

Envelopes

  • docusign.createEnvelope(ctx, options) - Create envelope with documents
  • docusign.createEmptyEnvelope(ctx, options) - Create empty envelope
  • docusign.createFromTemplate(ctx, options) - Create from template
  • docusign.sendEnvelope(ctx, userId, envelopeId) - Send an envelope
  • docusign.voidEnvelope(ctx, userId, envelopeId, reason) - Void an envelope
  • docusign.syncEnvelopeStatus(ctx, userId, envelopeId) - Sync status from DocuSign
  • docusign.getEnvelope(ctx, userId, envelopeId) - Get envelope from database
  • docusign.listEnvelopes(ctx, userId, paginationOpts?) - List user's envelopes

Webhooks

  • docusign.handleDocuSignWebhook(ctx, req) - Handle webhook (used internally)
  • docusign.getEnvelopeEvents(ctx, userId, envelopeId) - Get all events for an envelope
  • docusign.getLatestEvent(ctx, userId, envelopeId) - Get latest event

Custom

  • docusign.makeAPICall(ctx, userId, path, method?, body?, headers?) - Make any DocuSign API call

Security

  • ENCRYPTION_KEY is required - OAuth tokens are encrypted at rest
  • DOCUSIGN_HMAC_KEY is strongly recommended - validates webhook authenticity
  • Generate encryption key: openssl rand -hex 32

License

MIT

Support

File an issue

      {
        email: "[email protected]",
        name: "John Doe",
        recipientId: "1",
      },
    ],
    status: "created",
  },
);

}, });


### 3. Create an Envelope from Template

> **Important:** The `tabLabel` must match the **Data Label** field you set in your DocuSign template for each field. You can find this in the DocuSign template editor under the field's properties.

```ts
import { internalAction } from "./_generated/server";
import { internal } from "./_generated/api";

export const createFromTemplate = internalAction({
  args: {},
  handler: async (ctx) => {
    const userId = "user_123";

    return await ctx.runAction(
      internal.docusignIntegration.envelopes.createFromTemplate,
      {
        userId,
        templateId: "your-template-id",
        emailSubject: "Please sign this document",
        templateRoles: [
          {
            email: "[email protected]",
            name: "John Doe",
            roleName: "Signer",  // Must match role name in template
            tabs: {
              textTabs: [
                // tabLabel must match the "Data Label" in your template
                { tabLabel: "name", value: "John Doe" },
                { tabLabel: "date", value: "2024-01-15" },
              ],
            },
          },
        ],
        status: "created",
      },
    );
  },
});

4. Send an Envelope

import { internalAction } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";

export const sendEnvelope = internalAction({
  args: { envelopeId: v.string() },
  handler: async (ctx, args) => {
    const userId = "user_123";

    return await ctx.runAction(internal.docusignIntegration.envelopes.sendEnvelope, {
      userId,
      envelopeId: args.envelopeId,
    });
  },
});

5. Query Envelopes

import { internalQuery } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";

export const getEnvelope = internalQuery({
  args: { envelopeId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runQuery(internal.docusignIntegration.envelopes.getEnvelope, {
      envelopeId: args.envelopeId,
    });
  },
});

export const listEnvelopes = internalQuery({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runQuery(internal.docusignIntegration.envelopes.listEnvelopes, {
      userId: args.userId,
    });
  },
});

Webhook Configuration

To receive webhook events from DocuSign:

  1. In DocuSign Settings → Connect → Add Configuration
  2. Set the webhook URL to: https://your-app.convex.site/docusign/webhooks
  3. Select the envelope events you want to track:
    • Envelope Sent (event: envelope-sent, status: sent)
    • Envelope Delivered (event: envelope-delivered, status: delivered)
    • Envelope Completed (event: envelope-completed, status: completed)
    • Envelope Declined (event: envelope-declined, status: declined)
    • Envelope Voided (event: envelope-voided, status: voided)
    • Recipient Completed (event: recipient-completed)
  4. (Recommended) Enable HMAC signature validation in Connect settings

Important: DocuSign uses different naming conventions:

  • Webhook event types use hyphenated format (e.g., envelope-sent)
  • Envelope status field uses lowercase (e.g., sent)
  • The component automatically handles the conversion

The component will automatically store all events and update envelope statuses in your database.

API Reference

See the complete example in example/convex/example.ts.

Main Functions

OAuth (Public)

  • oauth.initiateOAuth - Get authorization URL (public action)
  • oauth.setupWithTokens - Set up connection with existing access/refresh tokens (public action)

OAuth (Internal)

  • oauth.completeOAuth - Complete OAuth flow (called by callback)
  • oauth.getValidConnection - Get valid connection with auto-refresh

Envelopes (Internal)

All envelope functions are internal for security:

  • envelopes.createEnvelopeWithDocuments - Create envelope with PDF/document attachments
  • envelopes.createEmptyEnvelope - Create empty envelope (for templates or manual document addition)
  • envelopes.createFromTemplate - Create from template with prefilled tabs
  • envelopes.sendEnvelope - Send an envelope
  • envelopes.voidEnvelope - Void an envelope
  • envelopes.syncEnvelopeStatus - Sync status from DocuSign
  • envelopes.getEnvelope - Get envelope from database
  • envelopes.listEnvelopes - List user's envelopes (with pagination)

Webhooks (Public Queries)

  • webhooks.getEnvelopeEvents - Get all events for an envelope
  • webhooks.getLatestEvent - Get latest event for an envelope

Custom API Calls

  • custom.makeAPICall - Make any DocuSign REST API call not covered by built-in functions
  • custom.getTemplate - Get template details including all available tabs/fields
  • custom.listTemplates - List all templates in the account
  • custom.getTemplateTabs - Get all tabs for a specific recipient in a template

Discovering Template Tabs

Before using a template, you can discover what Data Labels (tab labels) are available:

import { action } from "./_generated/server";
import { docusign } from "./docusign";

export const inspectTemplate = action({
  args: { templateId: v.string() },
  handler: async (ctx, args) => {
    // Get full template with all tabs
    const template = await docusign.getTemplate(ctx, "user_123", args.templateId);

    // Or get tabs for a specific recipient
    const tabs = await docusign.getTemplateTabs(ctx, "user_123", args.templateId, "1");

    // See what Data Labels are available
    console.log(
      "Available text fields:",
      tabs.textTabs?.map(t => t.tabLabel),
    );
    console.log(
      "Available checkboxes:",
      tabs.checkboxTabs?.map(t => t.tabLabel),
    );

    return tabs;
  },
});

Custom API Calls

Need to call a DocuSign endpoint not covered by the built-in functions? Use the custom.makeAPICall action:

import { action } from "./_generated/server";
import { components } from "./_generated/api";

// List all templates
export const listTemplates = action({
  args: {},
  handler: async ctx => {
    const userId = "user_123"; // Your auth logic

    return await ctx.runAction(components.docusignIntegration.custom.makeAPICall, {
      userId,
      path: "/templates",
      method: "GET",
    });
  },
});

// Get envelope documents
export const getEnvelopeDocuments = action({
  args: { envelopeId: v.string() },
  handler: async (ctx, args) => {
    const userId = "user_123";

    return await ctx.runAction(components.docusignIntegration.custom.makeAPICall, {
      userId,
      path: `/envelopes/${args.envelopeId}/documents`,
      method: "GET",
    });
  },
});

// Create a brand
export const createBrand = action({
  args: { brandName: v.string() },
  handler: async (ctx, args) => {
    const userId = "user_123";

    return await ctx.runAction(components.docusignIntegration.custom.makeAPICall, {
      userId,
      path: "/brands",
      method: "POST",
      body: {
        brandName: args.brandName,
        defaultBrandLanguage: "en",
      },
    });
  },
});

The makeAPICall function:

  • Automatically handles OAuth authentication and token refresh
  • Supports relative paths (e.g., /templates) or absolute paths
  • Accepts any HTTP method (GET, POST, PUT, DELETE, etc.)
  • Passes through request body and headers
  • Returns the raw JSON response from DocuSign

See DocuSign REST API documentation for all available endpoints.

Development

Run the example app:

npm install
npm run dev

License

MIT

Support

File an issue