Documenso vs SignWell vs DocuSign SDK: E-Signature APIs (2026)
TL;DR
Documenso is the open-source e-signature platform — self-hosted, signing API, templates, React components, embeddable signing, designed as the open-source DocuSign alternative. SignWell is the developer-friendly e-signature API — simple REST API, templates, bulk sending, embeddable, webhooks, built for developers. DocuSign is the enterprise e-signature platform — eSignature REST API, envelopes, templates, Connect webhooks, PowerForms, the industry standard. In 2026: Documenso for open-source self-hosted signing, SignWell for simple developer-first API, DocuSign for enterprise compliance.
Key Takeaways
- Documenso: 9K+ GitHub stars — open-source, self-hosted, React components
- SignWell: Developer-first — simple API, templates, bulk send, embed
- DocuSign: Industry standard — enterprise, compliance, 1B+ signatures
- Documenso provides the only fully open-source e-signature solution
- SignWell offers the simplest API for developers building signing flows
- DocuSign has the most compliance certifications and enterprise features
Documenso
Documenso — open-source e-signature:
Self-hosted setup
# Docker:
docker run -d \
--name documenso \
-p 3000:3000 \
-e NEXTAUTH_URL=http://localhost:3000 \
-e NEXTAUTH_SECRET=your-secret \
-e DATABASE_URL=postgresql://user:pass@db:5432/documenso \
-e NEXT_PRIVATE_SIGNING_PASSPHRASE=your-passphrase \
documenso/documenso:latest
API usage
// Documenso API:
const DOCUMENSO_URL = "https://app.documenso.com"
const DOCUMENSO_TOKEN = process.env.DOCUMENSO_API_TOKEN!
const headers = {
Authorization: `Bearer ${DOCUMENSO_TOKEN}`,
"Content-Type": "application/json",
}
// Create document from template:
const document = await fetch(`${DOCUMENSO_URL}/api/v1/documents`, {
method: "POST",
headers,
body: JSON.stringify({
title: "Service Agreement",
recipients: [
{
email: "signer@example.com",
name: "John Doe",
role: "SIGNER",
},
{
email: "viewer@example.com",
name: "Jane Smith",
role: "VIEWER",
},
],
}),
}).then((r) => r.json())
// Upload PDF:
const formData = new FormData()
formData.append("file", pdfBuffer, "agreement.pdf")
await fetch(`${DOCUMENSO_URL}/api/v1/documents/${document.id}/upload`, {
method: "POST",
headers: { Authorization: `Bearer ${DOCUMENSO_TOKEN}` },
body: formData,
})
// Add signature fields:
await fetch(`${DOCUMENSO_URL}/api/v1/documents/${document.id}/fields`, {
method: "POST",
headers,
body: JSON.stringify({
fields: [
{
type: "SIGNATURE",
recipientEmail: "signer@example.com",
page: 1,
positionX: 100,
positionY: 700,
width: 200,
height: 50,
},
{
type: "DATE",
recipientEmail: "signer@example.com",
page: 1,
positionX: 350,
positionY: 700,
width: 150,
height: 30,
},
{
type: "NAME",
recipientEmail: "signer@example.com",
page: 1,
positionX: 100,
positionY: 650,
width: 200,
height: 30,
},
],
}),
})
// Send for signing:
await fetch(`${DOCUMENSO_URL}/api/v1/documents/${document.id}/send`, {
method: "POST",
headers,
body: JSON.stringify({
message: "Please review and sign this agreement.",
}),
})
// Check document status:
const status = await fetch(
`${DOCUMENSO_URL}/api/v1/documents/${document.id}`,
{ headers }
).then((r) => r.json())
console.log(`Status: ${status.status}`) // PENDING, COMPLETED
Templates
// Create template:
const template = await fetch(`${DOCUMENSO_URL}/api/v1/templates`, {
method: "POST",
headers,
body: JSON.stringify({
title: "NDA Template",
roles: [
{ name: "Signer", type: "SIGNER" },
{ name: "Company Rep", type: "SIGNER" },
],
}),
}).then((r) => r.json())
// Create document from template:
const doc = await fetch(
`${DOCUMENSO_URL}/api/v1/templates/${template.id}/use`,
{
method: "POST",
headers,
body: JSON.stringify({
recipients: [
{ role: "Signer", email: "client@example.com", name: "Client" },
{ role: "Company Rep", email: "rep@company.com", name: "Rep" },
],
}),
}
).then((r) => r.json())
Embeddable signing
// Embed Documenso signing in your app:
function SigningEmbed({ signingUrl }: { signingUrl: string }) {
return (
<iframe
src={signingUrl}
style={{ width: "100%", height: "80vh", border: "none" }}
title="Sign Document"
/>
)
}
// Get signing URL via API:
const signingToken = await fetch(
`${DOCUMENSO_URL}/api/v1/documents/${docId}/signing-token`,
{
method: "POST",
headers,
body: JSON.stringify({ recipientEmail: "signer@example.com" }),
}
).then((r) => r.json())
const signingUrl = `${DOCUMENSO_URL}/sign/${signingToken.token}`
SignWell
SignWell — developer-friendly e-signature:
API setup
const SIGNWELL_API_KEY = process.env.SIGNWELL_API_KEY!
const headers = {
"X-Api-Key": SIGNWELL_API_KEY,
"Content-Type": "application/json",
}
Create and send documents
// Create document from file:
const document = await fetch("https://www.signwell.com/api/v1/documents", {
method: "POST",
headers,
body: JSON.stringify({
name: "Service Agreement",
recipients: [
{
id: "signer-1",
email: "client@example.com",
name: "John Doe",
placeholder_name: "Client",
},
],
files: [
{
name: "agreement.pdf",
file_base64: base64EncodedPdf,
},
],
fields: [
[
{
type: "signature",
required: true,
recipient_id: "signer-1",
page: 1,
x: 100,
y: 700,
width: 200,
height: 50,
},
{
type: "date",
required: true,
recipient_id: "signer-1",
page: 1,
x: 350,
y: 700,
width: 150,
height: 30,
},
{
type: "text",
required: true,
recipient_id: "signer-1",
label: "Full Name",
page: 1,
x: 100,
y: 650,
width: 200,
height: 30,
},
],
],
draft: false, // false = send immediately
reminders: true,
apply_signing_order: false,
custom_requester_name: "PkgPulse",
custom_requester_email: "signing@pkgpulse.com",
}),
}).then((r) => r.json())
console.log(`Document ID: ${document.id}`)
console.log(`Status: ${document.status}`) // pending, completed, expired
Templates
// Create from template:
const fromTemplate = await fetch(
"https://www.signwell.com/api/v1/document_templates/documents",
{
method: "POST",
headers,
body: JSON.stringify({
template_id: "template-uuid",
recipients: [
{
placeholder_name: "Client",
email: "client@example.com",
name: "John Doe",
},
{
placeholder_name: "Company",
email: "rep@company.com",
name: "Company Rep",
},
],
// Pre-fill template fields:
template_fields: [
{
api_id: "company_name_field",
value: "PkgPulse Inc.",
},
{
api_id: "contract_date_field",
value: "2026-03-09",
},
],
name: "Service Agreement - John Doe",
}),
}
).then((r) => r.json())
Bulk sending
// Send document to multiple recipients at once:
const bulk = await fetch(
"https://www.signwell.com/api/v1/document_templates/bulk_send",
{
method: "POST",
headers,
body: JSON.stringify({
template_id: "template-uuid",
bulk_send_list: [
{
recipients: [
{ placeholder_name: "Signer", email: "alice@example.com", name: "Alice" },
],
template_fields: [
{ api_id: "name_field", value: "Alice Johnson" },
],
},
{
recipients: [
{ placeholder_name: "Signer", email: "bob@example.com", name: "Bob" },
],
template_fields: [
{ api_id: "name_field", value: "Bob Smith" },
],
},
{
recipients: [
{ placeholder_name: "Signer", email: "carol@example.com", name: "Carol" },
],
template_fields: [
{ api_id: "name_field", value: "Carol Davis" },
],
},
],
}),
}
).then((r) => r.json())
Embedded signing and webhooks
// Get embedded signing URL:
const embedded = await fetch(
`https://www.signwell.com/api/v1/documents/${documentId}/embedded_signing_url`,
{
method: "POST",
headers,
body: JSON.stringify({
recipient_email: "signer@example.com",
redirect_url: "https://app.example.com/signed",
}),
}
).then((r) => r.json())
// Use in iframe or redirect:
const signingUrl = embedded.embedded_signing_url
// Webhook handler:
import express from "express"
const app = express()
app.use(express.json())
app.post("/webhooks/signwell", (req, res) => {
const { event, data } = req.body
switch (event) {
case "document_completed":
console.log(`Document ${data.document.id} completed!`)
// Download signed PDF, update database, etc.
break
case "document_viewed":
console.log(`${data.recipient.email} viewed the document`)
break
case "recipient_completed":
console.log(`${data.recipient.email} signed`)
break
case "document_expired":
console.log(`Document ${data.document.id} expired`)
break
}
res.sendStatus(200)
})
DocuSign
DocuSign — enterprise e-signature:
Setup
npm install docusign-esign
// lib/docusign.ts
import docusign from "docusign-esign"
const apiClient = new docusign.ApiClient()
apiClient.setBasePath("https://demo.docusign.net/restapi") // Use na*.docusign.net for prod
// JWT authentication:
async function getAccessToken() {
const results = await apiClient.requestJWTUserToken(
process.env.DOCUSIGN_INTEGRATION_KEY!,
process.env.DOCUSIGN_USER_ID!,
["signature", "impersonation"],
Buffer.from(process.env.DOCUSIGN_PRIVATE_KEY!, "base64"),
3600
)
apiClient.addDefaultHeader("Authorization", `Bearer ${results.body.access_token}`)
return results.body.access_token
}
export { apiClient, getAccessToken }
Create and send envelopes
import docusign from "docusign-esign"
import { apiClient, getAccessToken } from "@/lib/docusign"
async function sendForSignature(pdfBase64: string, signer: { email: string; name: string }) {
await getAccessToken()
const envelopesApi = new docusign.EnvelopesApi(apiClient)
// Create envelope:
const envelope = new docusign.EnvelopeDefinition()
envelope.emailSubject = "Please sign: Service Agreement"
envelope.emailBlurb = "Please review and sign the attached agreement."
// Add document:
const document = new docusign.Document()
document.documentBase64 = pdfBase64
document.name = "Service Agreement"
document.fileExtension = "pdf"
document.documentId = "1"
envelope.documents = [document]
// Add signer:
const signerObj = new docusign.Signer()
signerObj.email = signer.email
signerObj.name = signer.name
signerObj.recipientId = "1"
signerObj.routingOrder = "1"
// Add signature tab:
const signHere = new docusign.SignHere()
signHere.anchorString = "/sig1/"
signHere.anchorUnits = "pixels"
signHere.anchorXOffset = "0"
signHere.anchorYOffset = "0"
// Add date tab:
const dateField = new docusign.DateSigned()
dateField.anchorString = "/date1/"
dateField.anchorUnits = "pixels"
const tabs = new docusign.Tabs()
tabs.signHereTabs = [signHere]
tabs.dateSignedTabs = [dateField]
signerObj.tabs = tabs
const recipients = new docusign.Recipients()
recipients.signers = [signerObj]
envelope.recipients = recipients
envelope.status = "sent" // Send immediately
const result = await envelopesApi.createEnvelope(
process.env.DOCUSIGN_ACCOUNT_ID!,
{ envelopeDefinition: envelope }
)
return {
envelopeId: result.envelopeId,
status: result.status,
sentAt: result.statusDateTime,
}
}
Templates
// Create envelope from template:
async function sendFromTemplate(
templateId: string,
signer: { email: string; name: string },
prefillData: Record<string, string>
) {
await getAccessToken()
const envelopesApi = new docusign.EnvelopesApi(apiClient)
const envelope = new docusign.EnvelopeDefinition()
envelope.templateId = templateId
envelope.status = "sent"
// Template role (maps to template signer):
const templateRole = new docusign.TemplateRole()
templateRole.email = signer.email
templateRole.name = signer.name
templateRole.roleName = "Signer"
// Pre-fill tabs:
const textTabs = Object.entries(prefillData).map(([label, value]) => {
const tab = new docusign.Text()
tab.tabLabel = label
tab.value = value
return tab
})
const tabs = new docusign.Tabs()
tabs.textTabs = textTabs
templateRole.tabs = tabs
envelope.templateRoles = [templateRole]
return await envelopesApi.createEnvelope(
process.env.DOCUSIGN_ACCOUNT_ID!,
{ envelopeDefinition: envelope }
)
}
Embedded signing and webhooks
// Embedded signing (in your app):
async function getEmbeddedSigningUrl(envelopeId: string, signer: { email: string; name: string }) {
await getAccessToken()
const envelopesApi = new docusign.EnvelopesApi(apiClient)
const viewRequest = new docusign.RecipientViewRequest()
viewRequest.returnUrl = "https://app.example.com/signing-complete"
viewRequest.authenticationMethod = "none"
viewRequest.email = signer.email
viewRequest.userName = signer.name
viewRequest.recipientId = "1"
const result = await envelopesApi.createRecipientView(
process.env.DOCUSIGN_ACCOUNT_ID!,
envelopeId,
{ recipientViewRequest: viewRequest }
)
return result.url // Redirect user to this URL
}
// DocuSign Connect webhook handler:
import express from "express"
import { XMLParser } from "fast-xml-parser"
const app = express()
app.use(express.text({ type: "text/xml" }))
app.post("/webhooks/docusign", (req, res) => {
const parser = new XMLParser()
const data = parser.parse(req.body)
const envelope = data.DocuSignEnvelopeInformation.EnvelopeStatus
const status = envelope.Status
const envelopeId = envelope.EnvelopeID
switch (status) {
case "Completed":
console.log(`Envelope ${envelopeId} completed`)
// Download signed docs, update records
break
case "Sent":
console.log(`Envelope ${envelopeId} sent`)
break
case "Declined":
console.log(`Envelope ${envelopeId} declined`)
break
case "Voided":
console.log(`Envelope ${envelopeId} voided`)
break
}
res.sendStatus(200)
})
Feature Comparison
| Feature | Documenso | SignWell | DocuSign |
|---|---|---|---|
| License | AGPL v3 | Proprietary | Proprietary |
| Self-hosted | ✅ | ❌ | ❌ |
| REST API | ✅ | ✅ | ✅ |
| Templates | ✅ | ✅ | ✅ |
| Bulk sending | ❌ | ✅ | ✅ |
| Embedded signing | ✅ | ✅ | ✅ |
| Webhooks | ✅ | ✅ | ✅ (Connect) |
| Anchor tags | Basic | ✅ | ✅ |
| Signing order | ✅ | ✅ | ✅ |
| SMS delivery | ❌ | ❌ | ✅ |
| In-person signing | ❌ | ❌ | ✅ |
| Compliance | Basic | SOC 2 | SOC 2, HIPAA, eIDAS |
| Mobile signing | Web-based | Web-based | ✅ (native app) |
| Audit trail | ✅ | ✅ | ✅ (detailed) |
| Branding | ✅ | ✅ | ✅ |
| Free tier | ✅ (5 docs/month) | ✅ (3 docs/month) | ✅ (trial) |
| Pricing | Free / per-seat | Per-document | Per-envelope |
When to Use Each
Use Documenso if:
- Want a fully open-source, self-hosted e-signature solution
- Need to control your signing infrastructure and data
- Building a product that embeds signing as a core feature
- Prefer open-source with community governance
Use SignWell if:
- Want the simplest developer API for adding e-signatures
- Need bulk sending and template-based document generation
- Building integrations where signing is a feature, not the product
- Prefer straightforward REST API without SDK complexity
Use DocuSign if:
- Need enterprise compliance (HIPAA, eIDAS, SOC 2)
- Building in regulated industries (healthcare, finance, legal)
- Want the most feature-rich platform (in-person signing, SMS, PowerForms)
- Need the industry-standard e-signature that recipients trust
Methodology
GitHub stars and features as of March 2026. Feature comparison based on Documenso v1.x, SignWell API v1, and DocuSign eSignature REST API v2.1.