返回 Skill 列表
extension
分类: 开发与工程无需 API Key

documenso-core-workflow-b

实现基于Documenso模板的工作流程和直接签名链接。在创建可重用模板、从模板生成文档或实现直接签名体验时使用。触发词包括“documenso模板”、“签名链接”、“直接模板”、“可重用文档”、“模板工作流”。

person作者: jakexiaohubgithub

Documenso Core Workflow B: Templates & Direct Signing

Overview

Create reusable templates, generate documents from templates, and implement direct signing experiences with Documenso.

Prerequisites

  • Completed documenso-core-workflow-a
  • Understanding of template-based document generation
  • PDF template files ready

Instructions

Step 1: Create a Template

import { Documenso } from "@documenso/sdk-typescript";
import { openAsBlob } from "node:fs";

const documenso = new Documenso({
  apiKey: process.env.DOCUMENSO_API_KEY ?? "",
});

interface CreateTemplateInput {
  title: string;
  pdfPath: string;
  recipientRoles: Array<{
    name: string;  // e.g., "Employee", "Manager"
    role: "SIGNER" | "APPROVER" | "VIEWER" | "CC";
    signingOrder?: number;
  }>;
}

async function createTemplate(
  input: CreateTemplateInput
): Promise<string> {
  const pdfBlob = await openAsBlob(input.pdfPath);

  // Create the template
  const template = await documenso.templates.createV0({
    title: input.title,
    file: pdfBlob,
  });

  const templateId = template.templateId!;
  console.log(`Created template: ${templateId}`);

  // Add recipient placeholders
  for (const role of input.recipientRoles) {
    await documenso.templatesRecipients.createV0({
      templateId,
      name: role.name,
      role: role.role,
      signingOrder: role.signingOrder,
    });
    console.log(`Added role: ${role.name}`);
  }

  return templateId;
}

// Example: Create NDA template
const templateId = await createTemplate({
  title: "Non-Disclosure Agreement",
  pdfPath: "./templates/nda.pdf",
  recipientRoles: [
    { name: "Disclosing Party", role: "SIGNER", signingOrder: 1 },
    { name: "Receiving Party", role: "SIGNER", signingOrder: 2 },
  ],
});

Step 2: Add Template Fields

interface TemplateFieldInput {
  recipientIndex: number;  // Index of recipient in the roles array
  type: "SIGNATURE" | "INITIALS" | "NAME" | "EMAIL" | "DATE" | "TEXT";
  page: number;
  x: number;
  y: number;
  width?: number;
  height?: number;
}

async function addTemplateFields(
  templateId: string,
  fields: TemplateFieldInput[]
): Promise<void> {
  // Get template recipients to map indices to IDs
  const template = await documenso.templates.getV0({ templateId });
  const recipientIds = template.recipients?.map(r => r.id!) ?? [];

  for (const field of fields) {
    const recipientId = recipientIds[field.recipientIndex];
    if (!recipientId) {
      console.error(`No recipient at index ${field.recipientIndex}`);
      continue;
    }

    await documenso.templatesFields.createV0({
      templateId,
      recipientId,
      type: field.type,
      page: field.page,
      positionX: field.x,
      positionY: field.y,
      width: field.width ?? 200,
      height: field.height ?? 60,
    });
    console.log(`Added ${field.type} field for recipient ${field.recipientIndex}`);
  }
}

// Example: Add fields to NDA template
await addTemplateFields(templateId, [
  // Disclosing Party fields (index 0)
  { recipientIndex: 0, type: "SIGNATURE", page: 2, x: 100, y: 400 },
  { recipientIndex: 0, type: "NAME", page: 2, x: 100, y: 350 },
  { recipientIndex: 0, type: "DATE", page: 2, x: 350, y: 400 },
  // Receiving Party fields (index 1)
  { recipientIndex: 1, type: "SIGNATURE", page: 2, x: 100, y: 200 },
  { recipientIndex: 1, type: "NAME", page: 2, x: 100, y: 150 },
  { recipientIndex: 1, type: "DATE", page: 2, x: 350, y: 200 },
]);

Step 3: Generate Document from Template

interface UseTemplateInput {
  templateId: string;
  title?: string;
  recipients: Array<{
    email: string;
    name: string;
  }>;
  sendImmediately?: boolean;
}

async function createFromTemplate(
  input: UseTemplateInput
): Promise<{ documentId: string }> {
  // Use the template to create a new document
  const result = await documenso.envelopes.useV0({
    templateId: input.templateId,
    title: input.title,
    recipients: input.recipients.map((r, index) => ({
      email: r.email,
      name: r.name,
      signerIndex: index,
    })),
  });

  const documentId = result.envelopeId!;
  console.log(`Created document from template: ${documentId}`);

  // Send if requested
  if (input.sendImmediately) {
    await documenso.envelopes.distributeV0({
      envelopeId: documentId,
    });
    console.log("Document sent to recipients");
  }

  return { documentId };
}

// Example: Create NDA from template
const document = await createFromTemplate({
  templateId,
  title: "NDA - Acme Corp Partnership",
  recipients: [
    { email: "legal@yourcompany.com", name: "Your Company Legal" },
    { email: "partner@acme.com", name: "Acme Corp Representative" },
  ],
  sendImmediately: true,
});

Step 4: Direct Template Links

Direct templates allow signers to access and sign documents without pre-registration.

async function createDirectTemplateLink(
  templateId: string
): Promise<{ directLink: string }> {
  // Get template direct link settings
  const template = await documenso.templates.getV0({ templateId });

  // The direct link is available when template is published
  const directLink = template.directLink;

  if (!directLink) {
    console.log("Template direct link not enabled. Enable in dashboard.");
    throw new Error("Direct link not available");
  }

  return { directLink };
}

// Example usage with direct link
// Users can access: https://app.documenso.com/d/{directLinkToken}

Step 5: Embedding Signing Experience

// Get signing token for embedding
async function getSigningToken(
  documentId: string,
  recipientEmail: string
): Promise<{ signingToken: string; signingUrl: string }> {
  // Get document with recipients
  const doc = await documenso.documents.getV0({ documentId });

  const recipient = doc.recipients?.find(r => r.email === recipientEmail);
  if (!recipient) {
    throw new Error(`Recipient ${recipientEmail} not found`);
  }

  // The signing token can be used for embedding
  return {
    signingToken: recipient.signingToken!,
    signingUrl: recipient.signingUrl!,
  };
}

// For React embedding, use @documenso/embed-react
// npm install @documenso/embed-react

// React component example:
/*
import { EmbedDirectTemplate } from '@documenso/embed-react';

function SigningComponent({ signingToken }) {
  return (
    <EmbedDirectTemplate
      token={signingToken}
      host="https://app.documenso.com"
      onDocumentReady={() => console.log('Ready')}
      onDocumentCompleted={() => console.log('Completed')}
      onDocumentError={(error) => console.error(error)}
    />
  );
}
*/

Step 6: Pre-fill Template Fields

interface PrefillInput {
  templateId: string;
  recipients: Array<{
    email: string;
    name: string;
    prefillFields?: Array<{
      fieldId: string;
      value: string;
    }>;
  }>;
}

async function createWithPrefill(
  input: PrefillInput
): Promise<{ documentId: string }> {
  const result = await documenso.envelopes.useV0({
    templateId: input.templateId,
    recipients: input.recipients.map((r, index) => ({
      email: r.email,
      name: r.name,
      signerIndex: index,
      prefillFields: r.prefillFields,
    })),
  });

  return { documentId: result.envelopeId! };
}

// Example: Pre-fill contract details
await createWithPrefill({
  templateId,
  recipients: [
    {
      email: "client@example.com",
      name: "Client Name",
      prefillFields: [
        { fieldId: "company_name_field", value: "Acme Corporation" },
        { fieldId: "contract_amount_field", value: "$50,000" },
      ],
    },
  ],
});

Step 7: Duplicate and Modify Templates

async function duplicateTemplate(
  templateId: string,
  newTitle: string
): Promise<string> {
  const duplicate = await documenso.templates.duplicateV0({
    templateId,
    title: newTitle,
  });

  console.log(`Duplicated template: ${duplicate.templateId}`);
  return duplicate.templateId!;
}

// Example: Create region-specific variants
const usTemplateId = await duplicateTemplate(templateId, "NDA - US Version");
const euTemplateId = await duplicateTemplate(templateId, "NDA - EU Version");

Template Workflow Patterns

Pattern 1: Sales Contract Flow

// 1. Create template once
const salesContractTemplate = await createTemplate({
  title: "Sales Agreement",
  pdfPath: "./templates/sales-agreement.pdf",
  recipientRoles: [
    { name: "Sales Rep", role: "SIGNER", signingOrder: 1 },
    { name: "Customer", role: "SIGNER", signingOrder: 2 },
    { name: "Legal Review", role: "APPROVER", signingOrder: 3 },
  ],
});

// 2. Use for each deal
async function createSalesContract(deal: DealInfo) {
  return createFromTemplate({
    templateId: salesContractTemplate,
    title: `Sales Agreement - ${deal.customerName}`,
    recipients: [
      { email: deal.salesRepEmail, name: deal.salesRepName },
      { email: deal.customerEmail, name: deal.customerName },
      { email: "legal@company.com", name: "Legal Team" },
    ],
    sendImmediately: true,
  });
}

Pattern 2: Self-Service Signing

// Customer visits your site, fills form, and signs
async function selfServiceSigning(customerInfo: CustomerInfo) {
  // Create document from template with customer info pre-filled
  const doc = await createWithPrefill({
    templateId: selfServiceTemplateId,
    recipients: [
      {
        email: customerInfo.email,
        name: customerInfo.name,
        prefillFields: [
          { fieldId: "customer_name", value: customerInfo.name },
          { fieldId: "customer_address", value: customerInfo.address },
        ],
      },
    ],
  });

  // Get signing URL for immediate redirect
  const { signingUrl } = await getSigningToken(
    doc.documentId,
    customerInfo.email
  );

  return { signingUrl };
}

Output

  • Created reusable templates
  • Generated documents from templates
  • Direct signing links available
  • Embedded signing experience ready
  • Pre-filled field values

Error Handling

| Error | Cause | Solution | |-------|-------|----------| | Template not found | Invalid ID or deleted | Verify template exists | | Recipient mismatch | Wrong number of recipients | Match template roles | | Field not found | Invalid field ID for prefill | Get field IDs from template | | Direct link disabled | Feature not enabled | Enable in template settings | | Duplicate failed | Template in use | Try with different title |

Resources

Next Steps

For error handling patterns, see documenso-common-errors.