Documenso Migration Deep Dive
Current State
!npm list 2>/dev/null | head -10
Overview
Comprehensive guide for migrating to Documenso from other e-signature platforms (DocuSign, HelloSign, PandaDoc, Adobe Sign). Uses the Strangler Fig pattern for zero-downtime migration with feature flags and rollback support.
Prerequisites
- Current signing platform documented (APIs, templates, webhooks)
- Documenso account configured (see
documenso-install-auth)
- Feature flag infrastructure (LaunchDarkly, environment variables, etc.)
- Parallel run capability (both platforms active during migration)
Migration Strategy: Strangler Fig Pattern
Phase 1: Parallel Systems (Week 1-2)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Your App โโโโโโถโ Old Platform โ (100% traffic)
โ โ โโโโโโโโโโโโโโโ
โ โโโโโโถโ Documenso โ (shadow: log only, don't send)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
Phase 2: Gradual Cutover (Week 3-4)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Your App โโโโโโถโ Old Platform โ (50% traffic via feature flag)
โ โ โโโโโโโโโโโโโโโ
โ โโโโโโถโ Documenso โ (50% traffic)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
Phase 3: Full Migration (Week 5+)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Your App โโโโโโถโ Documenso โ (100% traffic)
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
Old platform decommissioned
Instructions
Step 1: Pre-Migration Assessment
interface MigrationAssessment {
platform: string;
activeTemplates: number;
documentsPerMonth: number;
webhookEndpoints: string[];
recipientRoles: string[];
fieldTypes: string[];
integrations: string[];
}
async function assessCurrentSystem(): Promise<MigrationAssessment> {
return {
platform: "DocuSign",
activeTemplates: 15,
documentsPerMonth: 200,
webhookEndpoints: [
"https://api.yourapp.com/webhooks/docusign",
],
recipientRoles: ["Signer", "CC", "In Person Signer"],
fieldTypes: ["Signature", "Date", "Text", "Checkbox", "Initial"],
integrations: ["Salesforce", "PostgreSQL"],
};
}
Step 2: Feature Mapping
| DocuSign | HelloSign | Documenso | Notes |
|---|
| Envelope | Signature Request | Document | Documenso v2 also has Envelopes |
| Template | Template | Template | Create via UI, use via API |
| Signer | Signer | SIGNER role | Same concept |
| CC | CC | CC role | Same concept |
| In Person | N/A | Direct Link | Use embedded signing |
| Tabs/Fields | Form Fields | Fields | SIGNATURE, TEXT, DATE, etc. |
| Connect (webhooks) | Callbacks | Webhooks | document.completed, etc. |
| PowerForms | N/A | Direct Links | Public signing URLs |
Step 3: Template Migration
interface TemplateDef {
name: string;
description: string;
recipientRoles: Array<{ role: string; placeholder: string }>;
fields: Array<{
recipientIndex: number;
type: string;
page: number;
x: number;
y: number;
width: number;
height: number;
}>;
}
const TEMPLATES: TemplateDef[] = [
{
name: "NDA โ Standard",
description: "Non-disclosure agreement template",
recipientRoles: [
{ role: "SIGNER", placeholder: "Counterparty" },
{ role: "CC", placeholder: "Legal Team" },
],
fields: [
{ recipientIndex: 0, type: "SIGNATURE", page: , : , : , : , : },
{ : , : , : , : , : , : , : },
{ : , : , : , : , : , : , : },
],
},
];
Step 4: Webhook Migration
const EVENT_MAPPING: Record<string, string> = {
"envelope-completed": "document.completed",
"envelope-sent": "document.sent",
"envelope-declined": "document.rejected",
"envelope-voided": "document.cancelled",
"recipient-completed": "document.signed",
"signature_request_all_signed": "document.completed",
"signature_request_sent": "document.sent",
"signature_request_declined": "document.rejected",
"signature_request_signed": "document.signed",
};
async function handleSigningEvent(source: "old" | "documenso", event: string, payload: any) {
const normalizedEvent = source === "old"
? EVENT_MAPPING[event] ?? event
: event;
switch (normalizedEvent) {
case "document.completed":
await onDocumentCompleted(payload);
break;
:
(payload);
;
}
}
Step 5: Dual-Write with Feature Flag
const USE_DOCUMENSO = process.env.USE_DOCUMENSO === "true";
async function sendForSigning(request: SigningRequest) {
if (USE_DOCUMENSO) {
return sendViaDocumenso(request);
}
return sendViaLegacy(request);
}
async function sendForSigningParallel(request: SigningRequest) {
const legacyResult = await sendViaLegacy(request);
try {
const doc = await documensoClient.documents.createV0({ title: request.title });
await documensoClient.documents.deleteV0(doc.documentId);
console.log("Documenso shadow test: OK");
} catch (err) {
console.error("Documenso shadow test: FAIL", err);
}
legacyResult;
}
Step 6: Rollback Procedure
export USE_DOCUMENSO=false
Migration Timeline
| Week | Phase | Action | Risk |
|---|
| 1 | Assessment | Inventory templates, webhooks, integrations | Low |
| 2 | Setup | Configure Documenso, recreate templates | Low |
| 3 | Shadow | Run parallel, compare results (no live traffic) | Low |
| 4 | Pilot | 10% of new documents via Documenso | Medium |
| 5 | Expand | 50% of new documents via Documenso | Medium |
| 6 | Cutover | 100% via Documenso, old platform read-only | Medium |
| 8 | Decommission | Remove old platform code and webhooks | Low |
Error Handling
| Migration Issue | Cause | Solution |
|---|
| Field position different | Different coordinate systems | Map percentage-based (Documenso) from pixel-based (old) |
| Webhook format change | Different payload structure | Use event normalizer/adapter |
| Template missing | Not recreated in Documenso | Create from template definitions |
| High error rate during cutover | Integration bug | Pause rollout, rollback, investigate |
Resources
Next Steps
Review related skills for comprehensive coverage of your new Documenso integration.