视觉画布设计
canvas-design
基于设计原则创作静态视觉作品,用于海报与品牌图形。
documenso-core-workflow-a【文档与办公】Implement Documenso document creation and recipient management workflows. Use when creating documents, managing recipients, adding signature fields, or building signing workflows with Documenso. Trigger with phrases like "documenso document", "create document", "add recipient", "documenso signer", "signature field".
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Complete workflow for creating documents, managing recipients, and configuring signature fields in Documenso.
documenso-install-auth setupdocumenso-sdk-patternsimport { Documenso } from "@documenso/sdk-typescript";
import { openAsBlob } from "node:fs";
const documenso = new Documenso({
apiKey: process.env.DOCUMENSO_API_KEY ?? "",
});
async function createDocument(
title: string,
pdfPath: string
): Promise<string> {
const pdfBlob = await openAsBlob(pdfPath);
const document = await documenso.documents.createV0({
title,
file: pdfBlob,
});
console.log(`Created document: ${document.documentId}`);
return document.documentId!;
}
interface RecipientInput {
email: string;
name: string;
role: "SIGNER" | "VIEWER" | "APPROVER" | "CC";
signingOrder?: number;
}
async function addRecipients(
documentId: string,
recipients: RecipientInput[]
): Promise<Map<string, string>> {
const recipientMap = new Map<string, string>(); // email -> recipientId
for (const recipient of recipients) {
const result = await documenso.documentsRecipients.createV0({
documentId,
email: recipient.email,
name: recipient.name,
role: recipient.role,
signingOrder: recipient.signingOrder,
});
recipientMap.set(recipient.email, result.recipientId!);
console.log(`Added ${recipient.role}: ${recipient.email}`);
}
return recipientMap;
}
// Example: Sequential signing order
const recipients: RecipientInput[] = [
{ email: "employee@company.com", name: "John Employee", role: "SIGNER", signingOrder: 1 },
{ email: "manager@company.com", name: "Jane Manager", role: "APPROVER", signingOrder: 2 },
{ email: "hr@company.com", name: "HR Department", role: "CC", signingOrder: 3 },
];
interface FieldInput {
recipientId: string;
type: "SIGNATURE" | "INITIALS" | "NAME" | "EMAIL" | "DATE" | "TEXT" | "CHECKBOX";
page: number;
x: number;
y: number;
width?: number;
height?: number;
required?: boolean;
placeholder?: string;
}
async function addFields(
documentId: string,
fields: FieldInput[]
): Promise<string[]> {
const fieldIds: string[] = [];
for (const field of fields) {
const result = await documenso.documentsFields.createV0({
documentId,
recipientId: field.recipientId,
type: field.type,
page: field.page,
positionX: field.x,
positionY: field.y,
width: field.width ?? getDefaultWidth(field.type),
height: field.height ?? getDefaultHeight(field.type),
});
fieldIds.push(result.fieldId!);
console.log(`Added ${field.type} field on page ${field.page}`);
}
return fieldIds;
}
function getDefaultWidth(type: string): number {
const widths: Record<string, number> = {
SIGNATURE: 200,
INITIALS: 80,
NAME: 180,
EMAIL: 200,
DATE: 120,
TEXT: 200,
CHECKBOX: 24,
};
return widths[type] ?? 150;
}
function getDefaultHeight(type: string): number {
const heights: Record<string, number> = {
SIGNATURE: 60,
INITIALS: 40,
NAME: 30,
EMAIL: 30,
DATE: 30,
TEXT: 30,
CHECKBOX: 24,
};
return heights[type] ?? 30;
}
interface CreateSigningDocumentInput {
title: string;
pdfPath: string;
recipients: Array<{
email: string;
name: string;
role: "SIGNER" | "VIEWER" | "APPROVER" | "CC";
signingOrder?: number;
fields: Array<{
type: "SIGNATURE" | "INITIALS" | "NAME" | "EMAIL" | "DATE" | "TEXT";
page: number;
x: number;
y: number;
}>;
}>;
sendImmediately?: boolean;
}
async function createSigningDocument(
input: CreateSigningDocumentInput
): Promise<{ documentId: string; signingUrls: Map<string, string> }> {
// Step 1: Create document with PDF
const pdfBlob = await openAsBlob(input.pdfPath);
const document = await documenso.documents.createV0({
title: input.title,
file: pdfBlob,
});
const documentId = document.documentId!;
// Step 2: Add recipients and collect their IDs
const recipientIds = new Map<string, string>();
for (const recipient of input.recipients) {
const result = await documenso.documentsRecipients.createV0({
documentId,
email: recipient.email,
name: recipient.name,
role: recipient.role,
signingOrder: recipient.signingOrder,
});
recipientIds.set(recipient.email, result.recipientId!);
}
// Step 3: Add fields for each recipient
for (const recipient of input.recipients) {
const recipientId = recipientIds.get(recipient.email)!;
for (const field of recipient.fields) {
await documenso.documentsFields.createV0({
documentId,
recipientId,
type: field.type,
page: field.page,
positionX: field.x,
positionY: field.y,
width: getDefaultWidth(field.type),
height: getDefaultHeight(field.type),
});
}
}
// Step 4: Send document if requested
if (input.sendImmediately) {
await documenso.documents.sendV0({ documentId });
console.log("Document sent to all recipients!");
}
// Step 5: Get signing URLs
const signingUrls = new Map<string, string>();
const doc = await documenso.documents.getV0({ documentId });
for (const recipient of doc.recipients ?? []) {
if (recipient.signingUrl) {
signingUrls.set(recipient.email!, recipient.signingUrl);
}
}
return { documentId, signingUrls };
}
// Example usage
const result = await createSigningDocument({
title: "Employment Contract",
pdfPath: "./contracts/employment.pdf",
recipients: [
{
email: "newhire@company.com",
name: "New Employee",
role: "SIGNER",
signingOrder: 1,
fields: [
{ type: "SIGNATURE", page: 3, x: 100, y: 600 },
{ type: "DATE", page: 3, x: 350, y: 600 },
{ type: "INITIALS", page: 1, x: 500, y: 750 },
{ type: "INITIALS", page: 2, x: 500, y: 750 },
],
},
{
email: "hr@company.com",
name: "HR Manager",
role: "APPROVER",
signingOrder: 2,
fields: [
{ type: "SIGNATURE", page: 3, x: 100, y: 700 },
{ type: "DATE", page: 3, x: 350, y: 700 },
],
},
],
sendImmediately: true,
});
console.log(`Document ID: ${result.documentId}`);
async function updateRecipient(
documentId: string,
recipientId: string,
updates: { email?: string; name?: string }
): Promise<void> {
await documenso.documentsRecipients.updateV0({
documentId,
recipientId,
...updates,
});
console.log(`Updated recipient ${recipientId}`);
}
// Example: Correct email typo
await updateRecipient(documentId, recipientId, {
email: "correct.email@company.com",
});
async function removeRecipient(
documentId: string,
recipientId: string
): Promise<void> {
await documenso.documentsRecipients.deleteV0({
documentId,
recipientId,
});
console.log(`Removed recipient ${recipientId}`);
}
| Role | Description | Signs Document |
|---|---|---|
| SIGNER | Primary signer | Yes |
| APPROVER | Must approve before completion | Yes |
| VIEWER | Can view but not sign | No |
| CC | Receives copy when complete | No |
| Error | Cause | Solution |
|---|---|---|
| Document not found | Invalid ID | Verify document exists |
| Recipient exists | Duplicate email | Update existing recipient |
| Invalid field position | Off-page coordinates | Check page dimensions |
| Cannot modify sent doc | Document already sent | Create new version |
| File too large | PDF exceeds limit | Compress or split PDF |
For template-based workflows, see documenso-core-workflow-b.