Skip to content

MemberJunction eSignature

Send documents for electronic signature from anywhere in MemberJunction — through one consistent API, backed by your choice of provider (DocuSign, PandaDoc, Dropbox Sign, and more), with full lifecycle tracking and a complete audit trail baked in.

The eSignature subsystem turns “get this document signed” into a single, provider-agnostic capability. Your application, an AI agent, or a no-code workflow asks MemberJunction to send a document; MemberJunction routes it to whichever signing provider you’ve configured, tracks the envelope from Draft to Completed, files the signed PDF back into your document store, and records every step. Switching providers — or adding a second one — is a configuration change, not a rewrite.


Every signing vendor has its own API, its own authentication scheme, its own status vocabulary, and its own webhook format. Wiring an application directly to one of them means coupling your business logic to that vendor forever. MemberJunction’s eSignature primitive sits between your app and the vendor:

flowchart LR
    App["Your App / Agent / Workflow"]
    Engine["eSignature Engine<br/>(one API)"]
    DS["DocuSign"]
    PD["PandaDoc"]
    DB["Dropbox Sign"]
    More["…future providers"]

    App --> Engine
    Engine --> DS
    Engine --> PD
    Engine --> DB
    Engine -.-> More

    style App fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Engine fill:#2d8659,stroke:#1a5c3a,color:#fff
    style DS fill:#444,stroke:#222,color:#fff
    style PD fill:#444,stroke:#222,color:#fff
    style DB fill:#444,stroke:#222,color:#fff
    style More fill:#666,stroke:#333,color:#fff,stroke-dasharray: 5 5

You write to the engine once. The engine speaks every provider’s dialect for you.


CapabilityDescription
One API, many providersSend, check status, download, and void — identical calls regardless of vendor.
Normalized status lifecycleEvery provider’s status vocabulary maps to one set: Draft → Sent → Delivered → Signed → Completed (plus Declined, Voided).
Secure credential handlingAPI keys, OAuth keys, and tokens are stored encrypted in the MJ Credential vault and decrypted only at send time. OAuth tokens are auto-refreshed.
Document provenanceSend a document straight from your MJ document store, and have the signed copy filed back automatically as a new version.
Complete audit trailEvery provider call and inbound webhook is logged with before/after status and full detail.
Inbound webhooksProviders push status changes to MemberJunction; payloads are signature-verified before they’re trusted.
No-code readyFour pre-built Actions expose signing to AI agents and workflow builders with zero code.

The subsystem is split into a vendor-neutral core primitive and one thin driver package per provider. You install the core plus only the providers you actually use.

graph TD
    subgraph core["Core primitive"]
        Base["@memberjunction/esignature<br/><i>contract + engines + types</i>"]
    end

    subgraph providers["Provider drivers"]
        DS["@memberjunction/esignature-docusign"]
        PD["@memberjunction/esignature-pandadoc"]
        DB["@memberjunction/esignature-dropboxsign"]
    end

    subgraph integration["Integration surface"]
        Actions["Send / Status / Download / Void<br/><i>(Core Actions)</i>"]
        Webhook["Webhook endpoint<br/><i>(MJ Server)</i>"]
    end

    DS --> Base
    PD --> Base
    DB --> Base
    Actions --> Base
    Webhook --> Base

    style Base fill:#2d8659,stroke:#1a5c3a,color:#fff
    style DS fill:#2d6a9f,stroke:#1a4971,color:#fff
    style PD fill:#2d6a9f,stroke:#1a4971,color:#fff
    style DB fill:#2d6a9f,stroke:#1a4971,color:#fff
    style Actions fill:#8a5a2d,stroke:#5c3a1a,color:#fff
    style Webhook fill:#8a5a2d,stroke:#5c3a1a,color:#fff
PackageWhat it isDocs
@memberjunction/esignatureThe core primitive — the provider contract, the normalized types, and the engines that orchestrate the envelope lifecycle and persistence. Start here.Base/README.md
@memberjunction/esignature-docusignDocuSign driver. JWT-grant OAuth. Supports the full feature set incl. templates, embedded signing, and Connect webhooks.Providers/DocuSign/README.md
@memberjunction/esignature-pandadocPandaDoc driver. API-key auth. Core send/status/download/void.Providers/PandaDoc/README.md
@memberjunction/esignature-dropboxsignDropbox Sign (formerly HelloSign) driver. API-key auth, with webhook support.Providers/DropboxSign/README.md

From the moment you ask for a signature to the moment the completed document lands back in your store:

sequenceDiagram
    participant App as Your App / Agent
    participant Eng as eSignature Engine
    participant Cred as Credential Vault
    participant Drv as Provider Driver
    participant Vendor as Signing Vendor
    participant Store as Document Store

    App->>Eng: Send "Contract.pdf" to alice@acme.com
    Eng->>Cred: Decrypt account credentials
    Cred-->>Eng: API key / OAuth token
    Eng->>Drv: initialize(config) + CreateEnvelope(...)
    Drv->>Vendor: REST call (vendor's API)
    Vendor-->>Drv: Envelope ID + "Sent"
    Drv-->>Eng: Normalized result
    Eng->>Eng: Create Signature Request + Recipients + Log

    Note over Vendor: Alice signs the document

    Vendor->>Eng: Webhook: "Completed"
    Eng->>Eng: Verify signature, update status, log

    App->>Eng: Download signed document
    Eng->>Drv: DownloadSignedDocument(...)
    Drv->>Vendor: Fetch signed PDF
    Vendor-->>Drv: PDF bytes
    Drv-->>Eng: Signed PDF
    Eng->>Store: File signed PDF as new version
    Eng-->>App: Signed PDF + provenance

Every provider’s native statuses collapse into one normalized progression so your application logic never has to know the difference:

stateDiagram-v2
    [*] --> Draft
    Draft --> Sent: send
    Sent --> Delivered: recipient opens
    Delivered --> Signed: recipient signs
    Signed --> Completed: all parties done
    Sent --> Declined: recipient declines
    Delivered --> Declined
    Draft --> Voided: cancel
    Sent --> Voided
    Delivered --> Voided
    Completed --> [*]
    Declined --> [*]
    Voided --> [*]

Six entities track everything. A consuming application links its own record (a contract, an application, a membership) to a Signature Request via the standard polymorphic EntityID / RecordID pair — no schema changes required on your side.

erDiagram
    SignatureProvider ||--o{ SignatureAccount : "instantiated by"
    SignatureAccount ||--o{ SignatureRequest : "sends through"
    SignatureRequest ||--o{ SignatureRequestDocument : "contains"
    SignatureRequest ||--o{ SignatureRequestRecipient : "addressed to"
    SignatureRequest ||--o{ SignatureRequestLog : "audited by"
    SignatureAccount }o--|| Credential : "secured by"
    SignatureRequestDocument }o--o| Artifact : "sourced from / filed to"

    SignatureProvider {
        string Name
        string ServerDriverKey
        bool   SupportsTemplates
        bool   SupportsEmbeddedSigning
    }
    SignatureAccount {
        string Name
        guid   CredentialID
        bool   IsDefault
    }
    SignatureRequest {
        string Name
        string Status
        string ExternalEnvelopeID
        guid   EntityID
        string RecordID
    }
    SignatureRequestDocument {
        string Name
        string Role
    }
    SignatureRequestRecipient {
        string Email
        string Status
    }
    SignatureRequestLog {
        string Operation
        bool   Success
    }
Entity (MJ name)Role
MJ: Signature ProvidersRegistry of provider types (DocuSign, PandaDoc, …) with their driver key and capability flags. Seeded via metadata.
MJ: Signature AccountsA configured instance of a provider — your DocuSign account, your PandaDoc workspace — pointing at an encrypted credential.
MJ: Signature RequestsThe envelope itself: title, status, the linked source record, and timing.
MJ: Signature Request DocumentsThe documents in the envelope, both the Source copies sent and the Signed copies received back.
MJ: Signature Request RecipientsThe signers, each with their own status and signed-at timestamp.
MJ: Signature Request LogsAn audit row for every provider call and webhook, with before/after status and detail.

Full field-by-field schema lives in Base/README.md.


You are…Use…
Building a no-code workflow or AI agentThe four pre-built ActionsSend Document for Signature, Get Signature Status, Download Signed Document, Void Signature Request. No code. See Base/README.md.
Writing server-side TypeScriptThe SignatureEngine from @memberjunction/esignature/server. See Base/README.md.
Building UI / reading metadata in the browserThe client-safe SignatureEngineBase from @memberjunction/esignature. See Base/README.md.

1. Install the core + the provider(s) you need:

Terminal window
npm install @memberjunction/esignature @memberjunction/esignature-docusign

2. Configure an account (one-time, in MJ metadata):

  • A Signature Provider row is seeded for you (DocuSign, PandaDoc, Dropbox Sign).
  • Create a Credential holding your encrypted vendor keys.
  • Create a Signature Account pointing at that credential.

See the provider-specific guide for exactly which credential fields each vendor needs: DocuSign · PandaDoc · Dropbox Sign.

3. Send a document — from an Action (no code) or from server code:

import { SignatureEngine } from '@memberjunction/esignature/server';
const result = await SignatureEngine.Instance.SendForSignature({
signatureAccountId: myAccountId,
title: 'Membership Agreement',
documents: [{ bytes: pdfBuffer, filename: 'agreement.pdf', contentType: 'application/pdf' }],
recipients: [{ email: 'alice@acme.com', name: 'Alice Smith' }],
// optionally link to the originating record:
entityId: contractsEntityId,
recordId: contractId,
contextUser,
});
console.log(result.status); // "Sent"

That’s the whole integration. Status updates arrive via webhook; the signed PDF is filed back automatically on download.


Not every vendor exposes every feature. The engine advertises each provider’s capabilities so callers (and agents) can adapt:

OperationDocuSignPandaDocDropbox Sign
Create envelope
Get status
Download signed
Void
Embedded signing
Templates
Inbound webhooks

  • Pluggable drivers. Each provider is a class registered with @RegisterClass(BaseSignatureProvider, '<Key>') and resolved at runtime by driver key — adding a vendor never touches the engine.
  • Client/server split. The browser-safe entry (@memberjunction/esignature) carries only the contract and metadata cache; the credential-decrypting, database-writing engine lives behind @memberjunction/esignature/server, so client bundles stay free of server-only dependencies.
  • Verify-if-configured webhooks. When a signing secret is configured, an inbound webhook whose signature doesn’t match is logged and does not update the envelope status; only verified (or, where no secret is configured, accepted-with-warning) events change state.
  • Graceful degradation. Optional operations a provider doesn’t support return a clear “not supported” result rather than throwing. Document file-back is best-effort — a download still returns bytes even if no storage account is configured.

For the full engineering detail — every type, every method, the credential flow, and the webhook contract — read the core primitive docs:


SubsystemHow eSignature uses it
@memberjunction/credentialsEncrypted storage + retrieval of vendor API keys and OAuth tokens.
@memberjunction/storageFiling source documents and signed PDFs as Artifacts.
@memberjunction/core-actionsThe four no-code Actions that expose signing to agents and workflows.
@memberjunction/core / core-entitiesThe metadata, entity, and engine foundations the whole subsystem is built on.