Optimize Documenso costs through efficient usage patterns, template reuse, and monitoring.
Documenso offers multiple plans:
| Plan | Best For | Key Limits |
|---|---|---|
| Free | Testing/Personal | Limited documents |
| Individual | Freelancers | Higher document limits |
| Teams | Small teams | Team collaboration |
| Enterprise | Large organizations | Custom limits |
// EXPENSIVE: Creating from scratch each time
async function createContractExpensive(data: ContractData) {
const doc = await client.documents.createV0({
title: data.title,
file: await generatePdf(data), // PDF generation cost
});
// Add recipients each time
await client.documentsRecipients.createV0({
documentId: doc.documentId!,
email: data.signerEmail,
name: data.signerName,
role: "SIGNER",
});
// Add fields each time
await client.documentsFields.createV0({
documentId: doc.documentId!,
recipientId: "...",
type: "SIGNATURE",
page: 1,
positionX: 100,
positionY: 600,
width: 200,
height: 60,
});
return doc;
}
// EFFICIENT: Use templates
async function createContractEfficient(data: ContractData) {
// Template already has fields and recipient placeholders
const envelope = await client.envelopes.useV0({
templateId: STANDARD_CONTRACT_TEMPLATE,
title: data.title,
recipients: [
{
email: data.signerEmail,
name: data.signerName,
signerIndex: 0,
},
],
});
return envelope;
}
// EXPENSIVE: Multiple documents for one transaction
// Creates 3 documents = 3x cost
async function handleDealExpensive(deal: Deal) {
await createDocument("NDA", deal);
await createDocument("MSA", deal);
await createDocument("SOW", deal);
}
// EFFICIENT: Combine into one document package
// Creates 1 document = 1x cost
async function handleDealEfficient(deal: Deal) {
// Combine PDFs into single document
const combinedPdf = await combinePdfs([
"./templates/nda.pdf",
"./templates/msa.pdf",
"./templates/sow.pdf",
]);
await createDocument("Deal Package", deal, combinedPdf);
}
// Clean up abandoned drafts to avoid confusion and track true usage
async function cleanupOldDrafts(daysOld = 30): Promise<number> {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
const docs = await client.documents.findV0({});
const oldDrafts = docs.documents?.filter(
(d) =>
d.status === "DRAFT" &&
new Date(d.createdAt!) < cutoffDate
) ?? [];
let deleted = 0;
for (const draft of oldDrafts) {
try {
await client.documents.deleteV0({ documentId: draft.id! });
deleted++;
} catch (error) {
console.warn(`Failed to delete draft ${draft.id}`);
}
}
console.log(`Deleted ${deleted} old drafts`);
return deleted;
}
// Schedule weekly cleanup
// cron: "0 0 * * 0" (Sunday midnight)
interface UsageMetrics {
period: { start: Date; end: Date };
documentsCreated: number;
documentsCompleted: number;
templateUsage: Map<string, number>;
activeRecipients: Set<string>;
}
async function trackUsage(): Promise<UsageMetrics> {
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const metrics: UsageMetrics = {
period: { start: startOfMonth, end: now },
documentsCreated: 0,
documentsCompleted: 0,
templateUsage: new Map(),
activeRecipients: new Set(),
};
// Iterate through documents
const docs = await client.documents.findV0({});
for (const doc of docs.documents ?? []) {
const createdAt = new Date(doc.createdAt!);
if (createdAt >= startOfMonth) {
metrics.documentsCreated++;
if (doc.status === "COMPLETED") {
metrics.documentsCompleted++;
}
// Track recipients
for (const recipient of doc.recipients ?? []) {
metrics.activeRecipients.add(recipient.email!);
}
}
}
return metrics;
}
// Alert when approaching limits
async function checkUsageLimits() {
const metrics = await trackUsage();
const MONTHLY_LIMIT = 100; // Your plan's limit
const usagePercent = (metrics.documentsCreated / MONTHLY_LIMIT) * 100;
if (usagePercent > 80) {
console.warn(`Usage at ${usagePercent.toFixed(0)}% of monthly limit!`);
// Send alert
}
}
// Reduce API calls by batching field creation
async function addFieldsEfficiently(
documentId: string,
fields: FieldConfig[]
): Promise<void> {
// Use batch endpoint when available
if (fields.length > 1) {
await client.documentsFields.createManyV0({
documentId,
fields: fields.map((f) => ({
recipientId: f.recipientId,
type: f.type as any,
page: f.page,
positionX: f.x,
positionY: f.y,
width: f.width ?? 200,
height: f.height ?? 60,
})),
});
} else if (fields.length === 1) {
const f = fields[0];
await client.documentsFields.createV0({
documentId,
recipientId: f.recipientId,
type: f.type as any,
page: f.page,
positionX: f.x,
positionY: f.y,
width: f.width ?? 200,
height: f.height ?? 60,
});
}
}
For high-volume use cases, self-hosting may be more cost-effective:
# docker-compose.yml for self-hosted Documenso
version: '3.8'
services:
documenso:
image: documenso/documenso:latest
ports:
- "3000:3000"
environment:
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- NEXT_PRIVATE_DATABASE_URL=${DATABASE_URL}
- NEXT_PRIVATE_SMTP_HOST=${SMTP_HOST}
volumes:
- ./cert.p12:/opt/documenso/cert.p12:ro
postgres:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Cost comparison (example):
// Generate monthly cost report
async function generateCostReport(month: Date): Promise<CostReport> {
const metrics = await trackUsage();
// Calculate costs (example rates)
const perDocumentCost = 0.5; // Example: $0.50 per document
const estimatedCost = metrics.documentsCreated * perDocumentCost;
const report = {
period: `${month.getFullYear()}-${String(month.getMonth() + 1).padStart(2, "0")}`,
metrics: {
documentsCreated: metrics.documentsCreated,
documentsCompleted: metrics.documentsCompleted,
completionRate:
(metrics.documentsCompleted / metrics.documentsCreated) * 100,
uniqueRecipients: metrics.activeRecipients.size,
},
costs: {
estimated: estimatedCost,
perDocument: perDocumentCost,
},
recommendations: [] as string[],
};
// Add recommendations
if (report.metrics.completionRate < 50) {
report.recommendations.push(
"Low completion rate - consider simplifying signing process"
);
}
if (metrics.documentsCreated > 500) {
report.recommendations.push(
"High volume - consider Enterprise plan or self-hosting"
);
}
return report;
}
| Cost Issue | Indicator | Solution |
|---|---|---|
| Overage charges | Exceeding limits | Upgrade plan or optimize |
| Unused features | Low completion rate | Simplify workflow |
| Duplicate documents | High creation rate | Use templates |
| Abandoned drafts | Many DRAFT status | Implement cleanup |
For architecture patterns, see documenso-reference-architecture.