| name | documenso-core-workflow-b |
| description | Implement Documenso template-based workflows and direct signing links.
Use when creating reusable templates, generating documents from templates,
or implementing direct signing experiences.
Trigger with phrases like "documenso template", "signing link",
"direct template", "reusable document", "template workflow".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
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;
role: "SIGNER" | "APPROVER" | "VIEWER" | "CC";
signingOrder?: number;
}>;
}
async function createTemplate(
input: CreateTemplateInput
): Promise<string> {
const pdfBlob = await openAsBlob(input.pdfPath);
const template = await documenso.templates.createV0({
title: input.title,
file: pdfBlob,
});
const templateId = template.templateId!;
console.log(`Created template: ${templateId}`);
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;
}
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;
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> {
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();
;
}
documenso..({
templateId,
recipientId,
: field.,
: field.,
: field.,
: field.,
: field. ?? ,
: field. ?? ,
});
.();
}
}
(templateId, [
{ : , : , : , : , : },
{ : , : , : , : , : },
{ : , : , : , : , : },
{ : , : , : , : , : },
{ : , : , : , : , : },
{ : , : , : , : , : },
]);
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 }> {
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}`);
if (input.sendImmediately) {
documenso..({
: documentId,
});
.();
}
{ documentId };
}
= ({
templateId,
: ,
: [
{ : , : },
{ : , : },
],
: ,
});
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 }> {
const template = await documenso.templates.getV0({ templateId });
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 };
}
Step 5: Embedding Signing Experience
async function getSigningToken(
documentId: string,
recipientEmail: string
): Promise<{ signingToken: string; signingUrl: string }> {
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`);
}
return {
signingToken: recipient.signingToken!,
signingUrl: recipient.signingUrl!,
};
}
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! };
}
await createWithPrefill({
templateId,
recipients: [
{
email: ,
: ,
: [
{ : , : },
{ : , : },
],
},
],
});
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!;
}
const usTemplateId = await duplicateTemplate(templateId, "NDA - US Version");
const euTemplateId = await duplicateTemplate(templateId, "NDA - EU Version");
Template Workflow Patterns
Pattern 1: Sales Contract Flow
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 },
],
});
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: },
],
: ,
});
}
Pattern 2: Self-Service Signing
async function selfServiceSigning(customerInfo: CustomerInfo) {
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 },
],
},
],
});
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.