mirror of
https://github.com/documenso/documenso.git
synced 2026-06-22 04:12:06 +10:00
feat: docs v2 (#2460)
Co-authored-by: Catalin Pit <catalinpit@gmail.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# deps
|
||||
/node_modules
|
||||
|
||||
# generated content
|
||||
.source
|
||||
|
||||
# test & build
|
||||
/coverage
|
||||
/.next/
|
||||
/out/
|
||||
/build
|
||||
*.tsbuildinfo
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
/.pnp
|
||||
.pnp.js
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# others
|
||||
.env*.local
|
||||
.vercel
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,45 @@
|
||||
# docs
|
||||
|
||||
This is a Next.js application generated with
|
||||
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
|
||||
|
||||
Run development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open http://localhost:3000 with your browser to see the result.
|
||||
|
||||
## Explore
|
||||
|
||||
In the project, you can see:
|
||||
|
||||
- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
|
||||
- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.
|
||||
|
||||
| Route | Description |
|
||||
| ------------------------- | ------------------------------------------------------ |
|
||||
| `app/(home)` | The route group for your landing page and other pages. |
|
||||
| `app/docs` | The documentation layout and pages. |
|
||||
| `app/api/search/route.ts` | The Route Handler for search. |
|
||||
|
||||
### Fumadocs MDX
|
||||
|
||||
A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
|
||||
|
||||
Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js and Fumadocs, take a look at the following
|
||||
resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
|
||||
features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "node_modules/@fumadocs/cli/dist/schema/src.json",
|
||||
"aliases": {
|
||||
"uiDir": "./components/ui",
|
||||
"componentsDir": "./components",
|
||||
"blockDir": "./components",
|
||||
"cssDir": "./styles",
|
||||
"libDir": "./lib"
|
||||
},
|
||||
"baseDir": "src",
|
||||
"uiLibrary": "radix-ui",
|
||||
"commands": {}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Certifications & Regulatory Compliance
|
||||
description: Documenso's compliance status for industry certifications and regulatory frameworks.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
### Compliance Status Overview
|
||||
|
||||
| Certification | Status |
|
||||
| -------------- | ---------------------- |
|
||||
| 21 CFR Part 11 | Compliant (Enterprise) |
|
||||
| SOC 2 | Compliant |
|
||||
| ISO 27001 | Planned |
|
||||
| HIPAA | Planned |
|
||||
|
||||
## 21 CFR Part 11
|
||||
|
||||
<Callout type="info">Status: Compliant (Enterprise License)</Callout>
|
||||
|
||||
21 CFR Part 11 is a regulation by the FDA that establishes the criteria for electronic records and electronic signatures to ensure their authenticity, integrity, and confidentiality in the pharmaceutical, medical device, and other FDA-regulated industries.
|
||||
|
||||
Read more about [21 CFR Part 11 with Documenso](https://documen.so/21-CFR-Part-11).
|
||||
|
||||
### Main Requirements
|
||||
|
||||
- Strong Identity Checks for each Signature
|
||||
- Signature and Audit Trails
|
||||
- User Access Management
|
||||
- Quality Assurance Documentation
|
||||
|
||||
## SOC 2
|
||||
|
||||
<Callout type="info">Status: [Compliant](https://documen.so/trust)</Callout>
|
||||
|
||||
SOC 2 is a framework for managing and auditing the security, availability, processing integrity, confidentiality, and data privacy in cloud and IT service organizations, established by the American Institute of Certified Public Accountants (AICPA).
|
||||
|
||||
## ISO 27001
|
||||
|
||||
<Callout type="warn">Status: [Planned](https://github.com/documenso/backlog/issues/26)</Callout>
|
||||
|
||||
ISO 27001 is an international standard for managing information security, specifying requirements for establishing, implementing, maintaining, and continually improving an information security management system (ISMS).
|
||||
|
||||
## HIPAA
|
||||
|
||||
<Callout type="warn">Status: [Planned](https://github.com/documenso/backlog/issues/25)</Callout>
|
||||
|
||||
The HIPAA (Health Insurance Portability and Accountability Act) is a U.S. law designed to protect patient health information's privacy and security and improve the healthcare system's efficiency and effectiveness.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Standards](/docs/compliance/standards) - Technical signing standards (PDF/A, PAdES, X.509)
|
||||
- [Signature Levels](/docs/compliance/signature-levels) - eIDAS and other signature level compliance
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - Enterprise licensing for compliance features
|
||||
- [GDPR](/docs/compliance/gdpr) - Data protection compliance
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: E-Sign Compliance
|
||||
description: Understand ESIGN, UETA, eIDAS, and other electronic signature laws that govern digital documents.
|
||||
---
|
||||
|
||||
## ESIGN Act (United States)
|
||||
|
||||
The Electronic Signatures in Global and National Commerce Act (ESIGN Act) is a U.S. federal law enacted in 2000. It ensures that electronic signatures and records have the same legal validity as paper documents and handwritten signatures in interstate and foreign commerce.
|
||||
|
||||
### Key Requirements
|
||||
|
||||
| Requirement | Description |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| **Intent to Sign** | Signers must demonstrate clear intent to sign the document |
|
||||
| **Consent** | All parties must agree to conduct the transaction electronically |
|
||||
| **Consumer Disclosure** | For consumer transactions, specific disclosures must be provided before obtaining consent |
|
||||
| **Record Retention** | Electronic records must be accurately preserved and accessible for later reference |
|
||||
| **Association** | The signature must be associated with the record being signed |
|
||||
|
||||
### Exclusions
|
||||
|
||||
The ESIGN Act does not apply to certain document types, including:
|
||||
|
||||
- Wills, codicils, and testamentary trusts
|
||||
- Family law documents (adoption, divorce)
|
||||
- Court orders and official court documents
|
||||
- Cancellation of utility services
|
||||
- Documents related to hazardous materials transportation
|
||||
|
||||
---
|
||||
|
||||
## UETA (United States)
|
||||
|
||||
The Uniform Electronic Transactions Act (UETA) is a model law adopted by 49 U.S. states (all except New York, which has its own Electronic Signatures and Records Act). UETA provides a legal framework for electronic signatures and records at the state level.
|
||||
|
||||
### Relationship to ESIGN
|
||||
|
||||
UETA and the ESIGN Act have similar requirements and purposes. The federal ESIGN Act allows states to modify or supersede certain ESIGN provisions if they adopt UETA or an equivalent law. In practice, the requirements for electronic signatures under both laws align closely.
|
||||
|
||||
### Key Requirements
|
||||
|
||||
- Intent to sign demonstrated by the signer
|
||||
- Consent to conduct transactions electronically
|
||||
- Retention of records in their original electronic form
|
||||
- Attribution of the signature to the signer
|
||||
|
||||
---
|
||||
|
||||
## eIDAS (European Union)
|
||||
|
||||
The Electronic Identification, Authentication and Trust Services (eIDAS) regulation governs electronic signatures across all EU member states. eIDAS establishes three levels of electronic signatures, each with different requirements and legal effects.
|
||||
|
||||
### Signature Levels
|
||||
|
||||
| Level | Description | Legal Effect |
|
||||
| ------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
||||
| **Simple (SES)** | Basic electronic signature with no specific technical requirements | Admissible as evidence; legal effect varies by use |
|
||||
| **Advanced (AES)** | Uniquely linked to signer, capable of identifying signer, under sole control | Higher evidentiary weight than SES |
|
||||
| **Qualified (QES)** | AES created by a qualified signature creation device, based on a qualified certificate | Equivalent to handwritten signature across the EU |
|
||||
|
||||
### Simple Electronic Signatures (SES)
|
||||
|
||||
SES is the baseline level. Any data in electronic form attached to or logically associated with other electronic data, used by the signatory to sign, qualifies as an SES. There are no specific technical requirements beyond demonstrating intent to sign.
|
||||
|
||||
### Advanced Electronic Signatures (AES)
|
||||
|
||||
AES must meet additional criteria:
|
||||
|
||||
- Uniquely linked to the signatory
|
||||
- Capable of identifying the signatory
|
||||
- Created using signature creation data under the signatory's sole control
|
||||
- Linked to the signed data in a way that detects subsequent changes
|
||||
|
||||
### Qualified Electronic Signatures (QES)
|
||||
|
||||
QES requires:
|
||||
|
||||
- A qualified certificate issued by a qualified trust service provider
|
||||
- Creation using a qualified electronic signature creation device
|
||||
- Identity verification compliant with eIDAS requirements
|
||||
|
||||
QES carries the same legal standing as a handwritten signature in all EU member states.
|
||||
|
||||
---
|
||||
|
||||
## Other Jurisdictions
|
||||
|
||||
Electronic signature laws exist in most countries. Below are selected examples:
|
||||
|
||||
| Jurisdiction | Framework | Notes |
|
||||
| ------------------ | --------------------------------------------- | ------------------------------------------------------------ |
|
||||
| **United Kingdom** | UK eIDAS / Electronic Communications Act 2000 | Post-Brexit, UK maintains eIDAS-like framework |
|
||||
| **Canada** | PIPEDA, provincial laws | Federal and provincial laws govern e-signatures |
|
||||
| **Australia** | Electronic Transactions Act 1999 | Generally technology-neutral approach |
|
||||
| **Switzerland** | ZertES | Swiss federal law with qualified signature requirements |
|
||||
| **Brazil** | MP 2200-2, ICP-Brasil | PKI-based framework for digital signatures |
|
||||
| **India** | IT Act 2000, Aadhaar e-KYC | Recognizes electronic signatures; Aadhaar-based verification |
|
||||
| **China** | Electronic Signature Law | Requires reliable electronic signatures for certain uses |
|
||||
| **Japan** | Electronic Signatures Act | Three-tier system similar to eIDAS |
|
||||
|
||||
Requirements vary significantly by jurisdiction. Some transactions may require specific signature types or have exclusions similar to the ESIGN Act.
|
||||
|
||||
---
|
||||
|
||||
## How Documenso Supports Compliance
|
||||
|
||||
Documenso provides features that support compliance with e-signature laws across jurisdictions:
|
||||
|
||||
### Intent to Sign
|
||||
|
||||
- Signers must actively interact with signature fields to apply their signature
|
||||
- The signing interface clearly indicates the document being signed
|
||||
- Signers receive a copy of the completed document
|
||||
|
||||
### Consent
|
||||
|
||||
- Recipients receive clear notification that they are being asked to sign electronically
|
||||
- The signing process requires affirmative action from the signer
|
||||
|
||||
### Record Retention
|
||||
|
||||
- Signed documents are stored and accessible to all parties
|
||||
- Original documents and audit trails are preserved
|
||||
- Documents can be downloaded in their signed form at any time
|
||||
|
||||
### Document Integrity
|
||||
|
||||
- All completed documents are cryptographically sealed
|
||||
- Any modification after signing invalidates the digital signature
|
||||
- PDF readers can verify the document has not been altered
|
||||
|
||||
### Signer Identification
|
||||
|
||||
- Email-based delivery establishes signer identity
|
||||
- Optional access codes add verification
|
||||
- Signing activity is logged with timestamps and metadata
|
||||
|
||||
---
|
||||
|
||||
## Audit Trails
|
||||
|
||||
Documenso maintains an audit trail for each document, recording:
|
||||
|
||||
| Event | Recorded Data |
|
||||
| ------------------ | -------------------------------------- |
|
||||
| Document creation | Timestamp, creator identity |
|
||||
| Recipient addition | Recipient details, assigned fields |
|
||||
| Document sent | Timestamp, delivery method |
|
||||
| Document viewed | Timestamp, viewer identity, IP address |
|
||||
| Field completed | Timestamp, field type, signer identity |
|
||||
| Document completed | Timestamp, final document hash |
|
||||
|
||||
The audit trail provides evidence of the signing process, including who signed, when they signed, and the sequence of events. This information supports the legal enforceability of the signed document.
|
||||
|
||||
---
|
||||
|
||||
## What Documenso Does NOT Provide
|
||||
|
||||
Documenso supports compliance with Simple Electronic Signature (SES) requirements. The following are not currently provided:
|
||||
|
||||
| Capability | Status |
|
||||
| ----------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **Qualified Electronic Signatures (QES)** | Not supported; requires integration with qualified trust service providers |
|
||||
| **Advanced Electronic Signatures (AES)** | Partial support; full AES requires identity verification services |
|
||||
| **Identity Verification (KYC)** | Not built-in; optional integrations may be available |
|
||||
| **Qualified Certificates** | Not issued; would require becoming a qualified trust service provider |
|
||||
| **Industry-Specific Compliance** | Features for specific regulations (e.g., healthcare, finance) depend on configuration |
|
||||
|
||||
For transactions requiring AES or QES, consult with legal counsel about appropriate solutions.
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This page provides general information about electronic signature laws for educational purposes. It does not constitute legal advice.
|
||||
|
||||
Electronic signature requirements vary by jurisdiction, transaction type, and specific circumstances. Some documents may have specific legal requirements that electronic signatures cannot satisfy.
|
||||
|
||||
Consult qualified legal counsel in your jurisdiction to determine whether electronic signatures are appropriate for your specific use case and what requirements must be met.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [Signature Levels](/docs/compliance/signature-levels) - Simple, Advanced, and Qualified electronic signatures explained
|
||||
- [Standards & Regulations](/docs/compliance/standards) - SOC 2, 21 CFR Part 11, and other compliance frameworks
|
||||
- [Signing Certificates](/docs/concepts/signing-certificates) - How documents are digitally signed and verified
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: GDPR
|
||||
description: Understand how Documenso handles GDPR compliance for data processing and storage.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Documenso's Role
|
||||
|
||||
When using Documenso for document signing, two distinct data processing roles apply:
|
||||
|
||||
| Role | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **Data Controller** | You (the organisation using Documenso) determine the purposes and means of processing |
|
||||
| **Data Processor** | Documenso processes personal data on your behalf according to your instructions |
|
||||
|
||||
As the data controller, you are responsible for:
|
||||
|
||||
- Obtaining appropriate consent or legal basis for processing
|
||||
- Informing data subjects about how their data is used
|
||||
- Responding to data subject access requests
|
||||
- Ensuring compliance with GDPR requirements
|
||||
|
||||
As the data processor, Documenso:
|
||||
|
||||
- Processes data only according to your instructions
|
||||
- Implements appropriate security measures
|
||||
- Assists with data subject requests when needed
|
||||
- Maintains records of processing activities
|
||||
|
||||
## Data Processing
|
||||
|
||||
Documenso processes personal data necessary to provide document signing services:
|
||||
|
||||
| Data Category | Examples | Purpose |
|
||||
| ------------------ | ---------------------------------------------- | --------------------------------------- |
|
||||
| **Identity Data** | Name, email address | User accounts, recipient identification |
|
||||
| **Document Data** | Uploaded PDFs, field values | Document storage and signing |
|
||||
| **Signature Data** | Signature images, signing timestamps | Recording signing actions |
|
||||
| **Audit Data** | IP addresses, browser information, action logs | Audit trail and verification |
|
||||
|
||||
Data is processed for the following purposes:
|
||||
|
||||
- Delivering documents to recipients
|
||||
- Recording signatures and other recipient actions
|
||||
- Generating signed documents with audit trails
|
||||
- Sending email notifications
|
||||
|
||||
## Data Storage Locations
|
||||
|
||||
Where your data is stored depends on how you use Documenso:
|
||||
|
||||
<Tabs items={['Documenso Cloud', 'Self-Hosted']}>
|
||||
<Tab value="Documenso Cloud">
|
||||
|
||||
For the hosted cloud service:
|
||||
|
||||
- Application data is stored in data centres within the European Union
|
||||
- Document storage uses EU-based infrastructure
|
||||
- Backups are maintained in geographically separate EU locations
|
||||
|
||||
Contact Documenso for specific information about sub-processors and data centre locations.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Self-Hosted">
|
||||
|
||||
When you self-host Documenso:
|
||||
|
||||
- You control all data storage locations
|
||||
- No data is transmitted to Documenso's infrastructure
|
||||
- You choose your own database, file storage, and backup locations
|
||||
|
||||
Self-hosting provides complete control over data residency, which may be required for certain compliance scenarios.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Data Subject Rights
|
||||
|
||||
GDPR grants individuals specific rights regarding their personal data. As the data controller, you are responsible for fulfilling these requests:
|
||||
|
||||
| Right | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **Access** | Data subjects can request a copy of their personal data |
|
||||
| **Rectification** | Data subjects can request correction of inaccurate data |
|
||||
| **Erasure** | Data subjects can request deletion of their data ("right to be forgotten") |
|
||||
| **Portability** | Data subjects can request their data in a machine-readable format |
|
||||
| **Restriction** | Data subjects can request limited processing of their data |
|
||||
| **Objection** | Data subjects can object to certain types of processing |
|
||||
|
||||
When you receive a data subject request, you can:
|
||||
|
||||
- Export user and document data from your Documenso account
|
||||
- Delete user accounts and associated documents
|
||||
- Contact Documenso support for assistance with cloud-hosted data
|
||||
|
||||
## Data Deletion
|
||||
|
||||
Documenso supports data deletion to help fulfill erasure requests:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="User Account Deletion">
|
||||
- Users can delete their own accounts
|
||||
- Account deletion removes profile data and authentication credentials
|
||||
- Team owners can remove members from teams
|
||||
</Accordion>
|
||||
<Accordion title="Document Deletion">
|
||||
- Document owners can delete documents in draft state
|
||||
- Completed documents can be deleted by the owner
|
||||
- Deletion removes the document, recipient data, and associated audit logs
|
||||
</Accordion>
|
||||
<Accordion title="Retention Considerations">
|
||||
For signed documents, you may need to balance deletion requests against:
|
||||
|
||||
- Legal requirements to retain signed contracts
|
||||
- Your organisation's record-keeping policies
|
||||
- The rights of other parties to the signed document
|
||||
|
||||
Consult with legal counsel to establish appropriate retention policies.
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Self-Hosting for GDPR Compliance
|
||||
|
||||
Self-hosting Documenso can simplify GDPR compliance:
|
||||
|
||||
- **Data residency** - Store all data in your chosen jurisdiction
|
||||
- **Sub-processor control** - No third-party data processors beyond your own infrastructure
|
||||
- **Direct access** - Full database access for data subject requests
|
||||
- **Retention control** - Implement custom data retention and deletion policies
|
||||
|
||||
See the [Self-Hosting Guide](/docs/self-hosting) for deployment options.
|
||||
|
||||
## Data Processing Agreement
|
||||
|
||||
A Data Processing Agreement (DPA) is a contract required by GDPR when a data controller engages a data processor.
|
||||
|
||||
<Tabs items={['Documenso Cloud', 'Self-Hosted']}>
|
||||
<Tab value="Documenso Cloud">
|
||||
|
||||
- A DPA is available upon request
|
||||
- Contact [support@documenso.com](mailto:support@documenso.com) to request a DPA
|
||||
- The DPA covers Documenso's obligations as a data processor
|
||||
|
||||
</Tab>
|
||||
<Tab value="Self-Hosted">
|
||||
|
||||
No DPA with Documenso is required since no personal data is processed by Documenso.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This documentation is provided for informational purposes only and does not constitute legal advice. GDPR compliance depends on your specific circumstances, including how you use Documenso, what data you process, and your organisation's obligations.
|
||||
|
||||
Consult with qualified legal counsel to:
|
||||
|
||||
- Determine your GDPR obligations
|
||||
- Draft appropriate privacy notices
|
||||
- Establish lawful bases for processing
|
||||
- Implement compliant data handling procedures
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [Standards & Regulations](/docs/compliance/standards) - eIDAS, ESIGN Act, and other compliance frameworks
|
||||
- [Self-Hosting Guide](/docs/self-hosting) - Deploy Documenso on your own infrastructure
|
||||
- [Security Settings](/docs/users/settings/security) - Configure authentication and security options
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Compliance
|
||||
description: Legal and regulatory compliance information for electronic signatures.
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="E-Sign Compliance"
|
||||
description="ESIGN Act, UETA, eIDAS, and electronic signature laws by jurisdiction."
|
||||
href="/docs/compliance/esign"
|
||||
/>
|
||||
<Card
|
||||
title="GDPR"
|
||||
description="Data protection requirements for processing personal data in the EU."
|
||||
href="/docs/compliance/gdpr"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## Additional Topics
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Standards & Regulations"
|
||||
description="SOC 2, 21 CFR Part 11, and other compliance frameworks."
|
||||
href="/docs/compliance/standards"
|
||||
/>
|
||||
<Card
|
||||
title="Signature Levels"
|
||||
description="Simple, Advanced, and Qualified electronic signatures under eIDAS."
|
||||
href="/docs/compliance/signature-levels"
|
||||
/>
|
||||
<Card
|
||||
title="Certifications"
|
||||
description="Compliance status for industry certifications and regulatory frameworks."
|
||||
href="/docs/compliance/certifications"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This documentation is provided for informational purposes only. It does not constitute legal advice and should not be relied upon as such.
|
||||
|
||||
Compliance requirements vary based on:
|
||||
|
||||
- Your jurisdiction and applicable laws
|
||||
- The type of documents being signed
|
||||
- Industry-specific regulations
|
||||
- The parties involved in the transaction
|
||||
|
||||
Consult with qualified legal counsel to determine the specific requirements for your use case.
|
||||
|
||||
## Related
|
||||
|
||||
- [Privacy Policy](/docs/policies/privacy) - How Documenso handles personal data
|
||||
- [Security](/docs/policies/security) - Security practices and measures
|
||||
- [Terms of Service](/docs/policies/terms) - Terms governing use of Documenso
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Compliance",
|
||||
"pages": ["esign", "standards", "signature-levels", "gdpr", "certifications"]
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
---
|
||||
title: Signature Levels
|
||||
description: Understand the three eIDAS signature levels — SES, AES, and QES — their requirements, legal effect, and when to use each.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
<Callout type="info">
|
||||
Documenso seals all signed documents cryptographically, regardless of signature level, to prevent
|
||||
any alterations after signing.
|
||||
</Callout>
|
||||
|
||||
### Compliance Status Overview
|
||||
|
||||
| Regulation | Status |
|
||||
| ------------ | --------- |
|
||||
| ESIGN / UETA | Compliant |
|
||||
| eIDAS SES | Compliant |
|
||||
| eIDAS AES | Planned |
|
||||
| eIDAS QES | Planned |
|
||||
| ZertES | Planned |
|
||||
|
||||
## U.S. ESIGN Act
|
||||
|
||||
<Callout type="info">Status: Compliant</Callout>
|
||||
|
||||
The Electronic Signatures in Global and National Commerce Act (ESIGN Act) is a U.S. federal law that ensures the legal validity and enforceability of electronic signatures and records in commerce.
|
||||
|
||||
### Main Requirements
|
||||
|
||||
- **Intent to Sign** - Parties must demonstrate their intent to sign
|
||||
- **Consent** - All parties must consent to the use of electronic signatures and records
|
||||
- **Consumer Disclosures** - Financial institutions must provide clear statements informing consumers before obtaining consent
|
||||
- **Record Retention** - Electronic records must be maintained for later access by signers
|
||||
- **Security** - Parties must take reasonable steps to ensure the security and integrity of electronic signatures and records
|
||||
|
||||
## UETA (Uniform Electronic Transactions Act)
|
||||
|
||||
<Callout type="info">Status: Compliant</Callout>
|
||||
|
||||
The Uniform Electronic Transactions Act provides a legal framework for the use of electronic signatures and records in electronic transactions, ensuring they have the same validity and enforceability as paper documents and handwritten signatures.
|
||||
|
||||
UETA shares the same core requirements as the [ESIGN Act](#us-esign-act).
|
||||
|
||||
## Simple Electronic Signatures (SES)
|
||||
|
||||
A Simple Electronic Signature is the most basic form of electronic signature. It includes any data in electronic form that is attached to or logically associated with other electronic data and used by the signatory to sign.
|
||||
|
||||
### Characteristics
|
||||
|
||||
| Aspect | Description |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **Technical Requirements** | No specific technical requirements beyond demonstrating intent to sign |
|
||||
| **Identity Verification** | None required; relies on email delivery or other indirect identification |
|
||||
| **Legal Status** | Admissible as evidence; cannot be denied legal effect solely because it is electronic |
|
||||
| **Examples** | Typed name, scanned signature image, checkbox acceptance, click-to-sign |
|
||||
|
||||
### When SES Is Appropriate
|
||||
|
||||
SES is suitable for many common business transactions:
|
||||
|
||||
- Standard contracts and agreements
|
||||
- Internal approvals and sign-offs
|
||||
- Terms of service acceptance
|
||||
- Non-disclosure agreements
|
||||
- Purchase orders and invoices
|
||||
- Employment documents (in most jurisdictions)
|
||||
|
||||
The legal validity of SES depends on the specific transaction and jurisdiction. Many everyday business documents do not require higher signature levels.
|
||||
|
||||
---
|
||||
|
||||
## Advanced Electronic Signatures (AES)
|
||||
|
||||
An Advanced Electronic Signature meets additional technical and procedural requirements that provide stronger evidence of the signer's identity and the document's integrity.
|
||||
|
||||
### Requirements
|
||||
|
||||
Under eIDAS, an AES must satisfy four criteria:
|
||||
|
||||
1. **Uniquely linked to the signatory** - The signature is associated with a specific individual
|
||||
2. **Capable of identifying the signatory** - The signature data reveals who signed
|
||||
3. **Created using signature creation data under the signatory's sole control** - Only the signer can create the signature (e.g., private key, secure device)
|
||||
4. **Linked to the data in such a way that any subsequent change is detectable** - Tampering invalidates the signature
|
||||
|
||||
### Characteristics
|
||||
|
||||
| Aspect | Description |
|
||||
| -------------------------- | -------------------------------------------------------------------------- |
|
||||
| **Technical Requirements** | Cryptographic signature with signer identification |
|
||||
| **Identity Verification** | Required; must establish signer identity through verification process |
|
||||
| **Legal Status** | Higher evidentiary weight than SES; stronger presumption of validity |
|
||||
| **Implementation** | Typically requires identity verification service and personal certificates |
|
||||
|
||||
### Compliance Status
|
||||
|
||||
<Callout type="warn">
|
||||
Status: [Planned](https://github.com/documenso/backlog/issues/9) via third party until [Let's
|
||||
Sign](https://github.com/documenso/backlog/issues/21) is realized.
|
||||
</Callout>
|
||||
|
||||
Current AES progress:
|
||||
|
||||
- Cryptographic signature sealing the document against tampering
|
||||
- Signing using dedicated hardware (Hardware Security Module)
|
||||
- Embedding signer identity in the cryptographic signature (planned)
|
||||
- Being a government-audited trusted qualified services provider (planned)
|
||||
|
||||
### When AES Is Appropriate
|
||||
|
||||
AES is used when stronger proof of identity and intent is needed:
|
||||
|
||||
- Financial services agreements
|
||||
- Real estate transactions (in some jurisdictions)
|
||||
- Healthcare consent forms
|
||||
- Government submissions
|
||||
- High-value contracts
|
||||
- Cross-border agreements within the EU
|
||||
|
||||
---
|
||||
|
||||
## Qualified Electronic Signatures (QES)
|
||||
|
||||
A Qualified Electronic Signature is the highest level of electronic signature under eIDAS. It is legally equivalent to a handwritten signature in all EU member states and carries a presumption of validity.
|
||||
|
||||
### Requirements
|
||||
|
||||
QES must meet all AES requirements plus:
|
||||
|
||||
1. **Qualified Certificate** - Issued by a Qualified Trust Service Provider (QTSP) that is accredited by an EU member state
|
||||
2. **Qualified Electronic Signature Creation Device (QSCD)** - The signature is created using hardware or software that meets specific security standards
|
||||
3. **Identity Verification** - In-person or equivalent remote verification compliant with eIDAS requirements
|
||||
|
||||
### Characteristics
|
||||
|
||||
| Aspect | Description |
|
||||
| -------------------------- | --------------------------------------------------------------------- |
|
||||
| **Technical Requirements** | Qualified certificate + qualified signature creation device |
|
||||
| **Identity Verification** | Strict verification by a Qualified Trust Service Provider |
|
||||
| **Legal Status** | Equivalent to handwritten signature across all EU member states |
|
||||
| **Implementation** | Requires integration with a QTSP; typically involves external service |
|
||||
|
||||
### Compliance Status
|
||||
|
||||
<Callout type="warn">
|
||||
Status: [Planned](https://github.com/documenso/backlog/issues/32) via third party until [Let's
|
||||
Sign](https://github.com/documenso/backlog/issues/21) is realized.
|
||||
</Callout>
|
||||
|
||||
### When QES Is Required
|
||||
|
||||
Certain transactions require or benefit from QES:
|
||||
|
||||
- Documents that legally require a handwritten signature under national law
|
||||
- Court filings and legal documents
|
||||
- Company formation documents
|
||||
- Land registry transactions
|
||||
- Notarized documents
|
||||
- Regulated financial transactions
|
||||
- Cross-border transactions requiring guaranteed recognition
|
||||
|
||||
---
|
||||
|
||||
## Comparison of Signature Levels
|
||||
|
||||
| Aspect | SES | AES | QES |
|
||||
| ------------------------- | ------------ | ------------------------ | --------------------- |
|
||||
| **Technical Complexity** | Low | Medium | High |
|
||||
| **Identity Verification** | None | Required | Strict (QTSP) |
|
||||
| **Legal Effect (EU)** | Admissible | Higher evidentiary value | Equal to handwritten |
|
||||
| **Cost** | Low | Medium | Higher |
|
||||
| **User Experience** | Simple | More steps | Most steps |
|
||||
| **Signer Requirements** | Email access | Identity verification | Certificate from QTSP |
|
||||
|
||||
### Legal Recognition
|
||||
|
||||
| Jurisdiction | SES | AES | QES |
|
||||
| ------------------ | ------------------------------- | -------------------------- | ------------------------------------ |
|
||||
| **European Union** | Valid, evidentiary value varies | Enhanced evidentiary value | Equivalent to handwritten |
|
||||
| **United States** | Valid under ESIGN/UETA | No formal distinction | No formal distinction |
|
||||
| **United Kingdom** | Valid | Enhanced value | Equivalent to handwritten (UK eIDAS) |
|
||||
| **Switzerland** | Valid | Valid | Equivalent to handwritten (ZertES) |
|
||||
|
||||
---
|
||||
|
||||
## What Documenso Provides
|
||||
|
||||
Documenso supports Simple Electronic Signatures (SES) with features that enhance evidentiary value:
|
||||
|
||||
### SES Features
|
||||
|
||||
- **Intent to Sign** - Signers actively interact with signature fields
|
||||
- **Email-Based Delivery** - Documents sent to specific email addresses
|
||||
- **Audit Trail** - Complete record of signing events, timestamps, and IP addresses
|
||||
- **Document Integrity** - Cryptographic sealing detects any post-signing modifications
|
||||
- **Record Retention** - Signed documents stored and accessible to all parties
|
||||
|
||||
### Additional Verification Options
|
||||
|
||||
- **Access Codes** - Require signers to enter a code before accessing documents
|
||||
- **Signing Order** - Control the sequence of signatures
|
||||
|
||||
### What Documenso Does Not Provide
|
||||
|
||||
| Capability | Status |
|
||||
| ----------------------------------------- | --------------------------------------------------------- |
|
||||
| **Qualified Electronic Signatures (QES)** | Not supported; requires QTSP integration |
|
||||
| **Advanced Electronic Signatures (AES)** | Partial; full AES requires identity verification services |
|
||||
| **Identity Verification (KYC)** | Not built-in |
|
||||
| **Qualified Certificates** | Not issued; would require QTSP status |
|
||||
|
||||
For transactions requiring AES or QES, you would need to integrate with external identity verification services or Qualified Trust Service Providers.
|
||||
|
||||
---
|
||||
|
||||
## ZertES (Swiss Federal Law)
|
||||
|
||||
<Callout type="warn">Status: [Planned](https://github.com/documenso/backlog/issues/34)</Callout>
|
||||
|
||||
ZertES is a Swiss federal law that regulates electronic signature compliance. It defines requirements similar to eIDAS for qualified electronic signatures within Switzerland.
|
||||
|
||||
---
|
||||
|
||||
## When You Need Higher Signature Levels
|
||||
|
||||
Consider using AES or QES when:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Legal Requirements">
|
||||
- National law requires a handwritten signature (QES may substitute)
|
||||
- Regulations specify signature requirements (e.g., certain financial or healthcare documents)
|
||||
- Cross-border enforceability is critical
|
||||
</Accordion>
|
||||
<Accordion title="Risk Factors">
|
||||
- High contract value or significant liability
|
||||
- Higher likelihood of disputes
|
||||
- Need for stronger non-repudiation
|
||||
- Counterparty or regulatory requirements specify higher levels
|
||||
</Accordion>
|
||||
<Accordion title="Industry Standards">
|
||||
- Financial services with regulatory oversight
|
||||
- Healthcare with patient consent requirements
|
||||
- Government or public sector contracts
|
||||
- Real estate transactions in regulated markets
|
||||
</Accordion>
|
||||
<Accordion title="Evaluating Your Needs">
|
||||
Most business transactions do not require AES or QES. Consider:
|
||||
|
||||
1. What does your jurisdiction require for this document type?
|
||||
2. What do your counterparties or customers expect?
|
||||
3. What is the risk if the signature is disputed?
|
||||
4. Does your industry have specific requirements?
|
||||
|
||||
When in doubt, consult with legal counsel to determine the appropriate signature level for your specific use case.
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This documentation is provided for informational purposes only and does not constitute legal advice.
|
||||
|
||||
The appropriate signature level for your documents depends on:
|
||||
|
||||
- Your jurisdiction and applicable laws
|
||||
- The type of document being signed
|
||||
- Industry-specific regulations
|
||||
- Contractual requirements from counterparties
|
||||
- Risk tolerance and dispute likelihood
|
||||
|
||||
Electronic signature requirements vary significantly across jurisdictions and document types. Some transactions have specific legal requirements that may mandate particular signature levels or exclude electronic signatures entirely.
|
||||
|
||||
Consult with qualified legal counsel to determine the signature level requirements for your specific use case.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [E-Sign Compliance](/docs/compliance/esign) - ESIGN Act, UETA, eIDAS, and electronic signature laws
|
||||
- [Signing Certificates](/docs/concepts/signing-certificates) - How documents are digitally signed and verified
|
||||
- [Standards & Regulations](/docs/compliance/standards) - SOC 2, 21 CFR Part 11, and other frameworks
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Standards & Regulations
|
||||
description: Key technical standards that ensure digital signatures are secure, interoperable, and valid long-term.
|
||||
---
|
||||
|
||||
## PDF/A for Archival
|
||||
|
||||
PDF/A is an ISO-standardized version of PDF designed for long-term archival of electronic documents. Unlike standard PDFs, PDF/A files are self-contained and do not rely on external resources.
|
||||
|
||||
Key characteristics:
|
||||
|
||||
- All fonts must be embedded
|
||||
- No external content references allowed
|
||||
- No encryption that would prevent future access
|
||||
- Metadata must be embedded in XMP format
|
||||
- Color spaces must be device-independent or include ICC profiles
|
||||
|
||||
PDF/A has several conformance levels (PDF/A-1, PDF/A-2, PDF/A-3) with increasing capabilities. PDF/A-3, for example, allows embedding of arbitrary file formats as attachments.
|
||||
|
||||
For signed documents intended for long-term storage, PDF/A ensures the document remains readable and verifiable years or decades after signing.
|
||||
|
||||
## PAdES (PDF Advanced Electronic Signatures)
|
||||
|
||||
PAdES is a set of standards (ETSI EN 319 142) that defines profiles for electronic signatures in PDF documents. It builds on the PDF signature capabilities defined in ISO 32000 and adds requirements for long-term validity.
|
||||
|
||||
PAdES defines several signature profiles:
|
||||
|
||||
| Profile | Description |
|
||||
| --------- | ---------------------------------------------------- |
|
||||
| PAdES-B | Basic signature with signing certificate |
|
||||
| PAdES-T | Adds a trusted timestamp |
|
||||
| PAdES-LT | Adds validation data (certificates, revocation info) |
|
||||
| PAdES-LTA | Adds long-term archival timestamps |
|
||||
|
||||
Each level builds upon the previous, with PAdES-LTA providing the strongest guarantees for long-term signature validity. The inclusion of validation data and archival timestamps allows signatures to be verified even after certificates expire or CAs cease operations.
|
||||
|
||||
## ISO 32000 (PDF Standard)
|
||||
|
||||
ISO 32000 is the international standard that defines the PDF format. It specifies the technical foundation for digital signatures in PDF documents.
|
||||
|
||||
Relevant signature capabilities defined in ISO 32000:
|
||||
|
||||
- Signature field dictionaries and appearance streams
|
||||
- Cryptographic signature handlers
|
||||
- Certificate and timestamp embedding
|
||||
- Incremental updates for signature preservation
|
||||
- Document modification detection
|
||||
|
||||
ISO 32000-2 (PDF 2.0) introduced additional features including support for more signature algorithms and improved encryption options.
|
||||
|
||||
## X.509 Certificates
|
||||
|
||||
X.509 is the standard format for public key certificates used in digital signatures. These certificates bind a public key to an identity and are issued by Certificate Authorities (CAs).
|
||||
|
||||
A typical X.509 certificate contains:
|
||||
|
||||
- Subject (identity information)
|
||||
- Issuer (the CA that issued the certificate)
|
||||
- Public key
|
||||
- Validity period (not before / not after dates)
|
||||
- Serial number
|
||||
- Signature algorithm
|
||||
- Extensions (key usage, policies, etc.)
|
||||
|
||||
For document signing, certificates typically include the "digital signature" key usage extension. Qualified certificates under eIDAS regulations have additional requirements and provide higher levels of assurance.
|
||||
|
||||
Certificate validation involves checking:
|
||||
|
||||
1. The certificate chain up to a trusted root CA
|
||||
2. That no certificate in the chain has expired
|
||||
3. Revocation status via CRL or OCSP
|
||||
|
||||
## RFC 3161 (Timestamping)
|
||||
|
||||
RFC 3161 defines the Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP). Timestamps prove that a document existed in a specific state at a particular point in time.
|
||||
|
||||
A timestamp token contains:
|
||||
|
||||
- Hash of the signed data
|
||||
- Time of issuance (from a trusted time source)
|
||||
- Identifier of the Time Stamping Authority (TSA)
|
||||
- TSA's digital signature
|
||||
|
||||
Timestamps serve two purposes in document signing:
|
||||
|
||||
1. **Proof of existence**: Demonstrates the document was signed before a certain time
|
||||
2. **Signature validity extension**: Allows signature verification after the signing certificate expires
|
||||
|
||||
Without a trusted timestamp, a signature can only be verified while the signing certificate remains valid. With a timestamp, the signature remains verifiable as long as the timestamp can be validated.
|
||||
|
||||
## What Documenso Implements
|
||||
|
||||
Documenso implements digital signatures with the following characteristics:
|
||||
|
||||
- **PDF signatures**: Documents are signed using the PDF signature capabilities defined in ISO 32000
|
||||
- **X.509 certificates**: Signatures use X.509 certificates for signer identification
|
||||
- **Timestamps**: RFC 3161 timestamps can be applied to signatures
|
||||
- **Signature visualization**: Signed documents include visual signature representations
|
||||
|
||||
For specific implementation details and configuration options, refer to the [signing certificates](/signing-certificates/overview) documentation.
|
||||
|
||||
Self-hosted deployments can configure their own signing certificates and timestamp authorities to meet specific compliance requirements.
|
||||
|
||||
## Related
|
||||
|
||||
- [Legal Validity](/compliance/legal-validity) - Legal frameworks for electronic signatures
|
||||
- [Signing Certificates Overview](/signing-certificates/overview) - Certificate configuration
|
||||
- [Audit Log](/features/audit-log) - Document activity tracking
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: Document Lifecycle
|
||||
description: Track document progress through draft, pending, completed, and rejected states.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## Document States
|
||||
|
||||
A document can be in one of four states:
|
||||
|
||||
| State | Description |
|
||||
| ------------- | ----------------------------------------------------------------- |
|
||||
| **Draft** | Document is being prepared and has not been sent |
|
||||
| **Pending** | Document has been sent and is awaiting recipient actions |
|
||||
| **Completed** | All recipients have completed their required actions |
|
||||
| **Rejected** | A recipient has rejected the document (when rejection is enabled) |
|
||||
|
||||
## How a Document Moves Through States
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Draft -- Send --> Pending
|
||||
Pending -- All recipients complete --> Completed
|
||||
Pending -- Recipient rejects --> Rejected
|
||||
```
|
||||
|
||||
## Draft
|
||||
|
||||
When you upload a document or create one from a template, it starts in the **Draft** state. In this state, you can:
|
||||
|
||||
- Add and remove recipients
|
||||
- Assign roles to recipients (signer, approver, viewer, CC)
|
||||
- Add, move, and configure fields
|
||||
- Set signing order
|
||||
- Configure document settings (expiration, reminders, rejection)
|
||||
- Delete the document
|
||||
|
||||
A draft document is only visible to you (the owner) and team members with appropriate permissions. Recipients cannot see or access the document until you send it.
|
||||
|
||||
**Transition:** A draft becomes **Pending** when you send it to recipients.
|
||||
|
||||
## Pending
|
||||
|
||||
Once sent, a document enters the **Pending** state. Recipients receive email notifications with links to view and complete their assigned actions.
|
||||
|
||||
While pending, you can:
|
||||
|
||||
- View recipient progress
|
||||
- Resend notifications to recipients
|
||||
- Void the document (cancels all pending actions)
|
||||
|
||||
<Callout type="info">
|
||||
You cannot modify the document content, recipients, or fields while it is pending.
|
||||
</Callout>
|
||||
|
||||
**Transitions:**
|
||||
|
||||
- Becomes **Completed** when all recipients finish their required actions
|
||||
- Becomes **Rejected** if any recipient rejects the document (requires rejection to be enabled)
|
||||
|
||||
## Completed
|
||||
|
||||
A document reaches the **Completed** state when all recipients have fulfilled their roles:
|
||||
|
||||
- Signers have signed
|
||||
- Approvers have approved
|
||||
- Viewers have viewed (if view confirmation is required)
|
||||
|
||||
At completion:
|
||||
|
||||
- All parties receive a copy of the signed document
|
||||
- The document is sealed with a digital certificate
|
||||
- An audit log is attached showing all actions taken
|
||||
|
||||
<Callout type="info">
|
||||
Completed documents cannot be modified. You can download the signed PDF or view the audit trail.
|
||||
</Callout>
|
||||
|
||||
## Rejected
|
||||
|
||||
If you enable document rejection in settings, recipients can reject instead of signing. When any recipient rejects:
|
||||
|
||||
- The document immediately moves to **Rejected** state
|
||||
- Other pending recipients can no longer act on the document
|
||||
- The document owner is notified
|
||||
|
||||
<Callout type="info">
|
||||
Rejected documents cannot be modified or reactivated. To proceed, you need to create a new
|
||||
document.
|
||||
</Callout>
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- [Recipient Roles](/docs/concepts/recipient-roles) - The different roles recipients can have
|
||||
- [Field Types](/docs/concepts/field-types) - Fields you can add to documents
|
||||
- [Signing Workflow](/docs/concepts/signing-workflow) - How the signing process works for recipients
|
||||
@@ -0,0 +1,314 @@
|
||||
---
|
||||
title: Field Types
|
||||
description: Placeholder types for capturing signatures, text, dates, and selections during signing.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## Field Types Overview
|
||||
|
||||
| Field Type | Description | Auto-filled |
|
||||
| ---------- | ------------------------------------------------- | ----------- |
|
||||
| Signature | Recipient's signature (drawn, typed, or uploaded) | No |
|
||||
| Initials | Recipient's initials | No |
|
||||
| Email | Recipient's email address | Yes |
|
||||
| Name | Recipient's full name | Yes |
|
||||
| Date | Date the field was completed | Yes |
|
||||
| Text | Free-form text input | No |
|
||||
| Number | Numeric input with optional validation | No |
|
||||
| Radio | Single selection from a list of options | No |
|
||||
| Checkbox | Multiple selections from a list of options | No |
|
||||
| Dropdown | Single selection from a dropdown menu | No |
|
||||
|
||||
## Signature
|
||||
|
||||

|
||||
|
||||
The signature field captures the recipient's legally binding signature. Recipients can:
|
||||
|
||||
- **Draw** their signature using a mouse or touchscreen
|
||||
- **Type** their name and select a font style
|
||||
- **Upload** an image of their signature
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------- |
|
||||
| Required | Whether the field must be completed before signing |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Contract execution
|
||||
- Agreement acceptance
|
||||
- Authorization approvals
|
||||
|
||||
<Callout type="info">Each signer must have at least one Signature field assigned to them.</Callout>
|
||||
|
||||
## Initials
|
||||
|
||||
The initials field captures abbreviated signatures, typically used to acknowledge individual pages or clauses.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | -------------------------------------- |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Page acknowledgment
|
||||
- Clause acceptance
|
||||
- Change or amendment approval
|
||||
|
||||
## Email
|
||||
|
||||
The email field displays the recipient's email address. This field is automatically populated with the email address used to send the signing request.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | --------------------------------------------------- |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field (recommended for auto-filled values) |
|
||||
| Label | Display text shown above the field |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Contact information sections
|
||||
- Identity verification
|
||||
- Record keeping
|
||||
|
||||
## Name
|
||||
|
||||
The name field captures the recipient's full name. When the recipient has a name on file, the field can be auto-populated.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | -------------------------------------- |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Signature blocks
|
||||
- Party identification
|
||||
- Contact details
|
||||
|
||||
## Date
|
||||
|
||||
The date field records when the recipient completed the field or signed the document. By default, it auto-fills with the current date.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | -------------------------------------- |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Signature date
|
||||
- Agreement effective date
|
||||
- Timestamp records
|
||||
|
||||
## Text
|
||||
|
||||

|
||||
|
||||
The text field accepts free-form text input from recipients. Use this for any information that doesn't fit other field types.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| --------------- | ------------------------------------------ |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
| Placeholder | Hint text shown when the field is empty |
|
||||
| Default value | Pre-filled text that recipients can modify |
|
||||
| Character limit | Maximum number of characters allowed |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
| Line height | Spacing between lines of text |
|
||||
| Letter spacing | Spacing between characters |
|
||||
|
||||
### Rules
|
||||
|
||||
- A field cannot be both required and read-only at the same time
|
||||
- A read-only field must have a default text value (it cannot be empty)
|
||||
- The field is inserted automatically into the document if there is a default text value
|
||||
- The text field character count cannot exceed the character limit
|
||||
- The signer cannot modify a read-only field
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Address input
|
||||
- Company names
|
||||
- Job titles
|
||||
- Custom information
|
||||
|
||||
## Number
|
||||
|
||||
The number field accepts numeric input with optional validation constraints.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | -------------------------------------------- |
|
||||
| Required | Whether the field must be completed |
|
||||
| Read-only | Lock the field with a pre-filled value |
|
||||
| Label | Display text shown above the field |
|
||||
| Placeholder | Hint text shown when the field is empty |
|
||||
| Default value | Pre-filled number that recipients can modify |
|
||||
| Minimum value | Lowest allowed number |
|
||||
| Maximum value | Highest allowed number |
|
||||
| Number format | Display format for the number |
|
||||
| Text alignment | Left, center, or right alignment |
|
||||
|
||||
### Rules
|
||||
|
||||
- The value must be a number
|
||||
- A field cannot be both required and read-only at the same time
|
||||
- A read-only field must have a default number value
|
||||
- If a default number and a maximum value are set, the default must be less than the maximum
|
||||
- If a default number and a minimum value are set, the default must be greater than the minimum
|
||||
- The value must match the number format if a number format is set
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Quantities
|
||||
- Pricing
|
||||
- Phone numbers
|
||||
- Employee IDs
|
||||
|
||||
## Radio
|
||||
|
||||
The radio field presents a list of options where the recipient can select exactly one.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ---------------------------------------- |
|
||||
| Required | Whether a selection must be made |
|
||||
| Read-only | Lock the field with a pre-selected value |
|
||||
| Label | Display text shown above the field |
|
||||
| Options | List of selectable values |
|
||||
| Default selection | Pre-selected option |
|
||||
| Direction | Vertical or horizontal layout |
|
||||
|
||||
### Rules
|
||||
|
||||
- A field cannot be both required and read-only at the same time
|
||||
- A read-only field must have at least one option
|
||||
- The field auto-signs if there is a default value
|
||||
- The signer cannot select a value that's not in the options list
|
||||
- Only one option can be selected at a time
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Yes/No questions
|
||||
- Single-choice selections
|
||||
- Status indicators
|
||||
- Plan or tier selection
|
||||
|
||||
## Checkbox
|
||||
|
||||

|
||||
|
||||
The checkbox field presents a list of options where the recipient can select multiple items.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| Required | Whether at least one selection must be made |
|
||||
| Read-only | Lock the field with pre-selected values |
|
||||
| Label | Display text shown above the field |
|
||||
| Options | List of selectable values |
|
||||
| Default selections | Pre-selected options |
|
||||
| Validation rule | Rules for minimum/maximum selections |
|
||||
| Direction | Vertical or horizontal layout |
|
||||
|
||||
### Rules
|
||||
|
||||
- A field cannot be both required and read-only at the same time
|
||||
- A read-only field must have at least one checked option
|
||||
- The field auto-signs if there are default values
|
||||
- The validation rule enforces selection counts: "At least", "At most", or "Exactly" a specified number of options
|
||||
- The signer cannot select a value that's not in the options list
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Terms and conditions acceptance
|
||||
- Multiple acknowledgments
|
||||
- Feature selection
|
||||
- Preference lists
|
||||
|
||||
## Dropdown
|
||||
|
||||

|
||||
|
||||
The dropdown field presents a list of options in a collapsible menu. Recipients select one option from the list.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | ---------------------------------------- |
|
||||
| Required | Whether a selection must be made |
|
||||
| Read-only | Lock the field with a pre-selected value |
|
||||
| Label | Display text shown above the field |
|
||||
| Options | List of selectable values |
|
||||
| Default value | Pre-selected option |
|
||||
|
||||
### Rules
|
||||
|
||||
- A field cannot be both required and read-only at the same time
|
||||
- A read-only field must have a default value
|
||||
- The default value must be one of the options
|
||||
- The field auto-signs if there is a default value
|
||||
- The signer cannot select a value that's not in the options list
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- Country or state selection
|
||||
- Department selection
|
||||
- Category classification
|
||||
- Status selection
|
||||
|
||||
## Common Configuration Options
|
||||
|
||||
All field types share these base configuration options:
|
||||
|
||||
| Option | Description | Default |
|
||||
| --------- | --------------------------------------------------- | ------- |
|
||||
| Required | Recipient must complete the field to finish signing | `false` |
|
||||
| Read-only | Field value cannot be changed by the recipient | `false` |
|
||||
| Label | Text displayed above or near the field | None |
|
||||
| Font size | Size of the text in the field (8-96px) | 12px |
|
||||
|
||||
## Validation
|
||||
|
||||
Fields validate input based on their type and configuration:
|
||||
|
||||
- **Required fields** must be completed before the recipient can finish signing
|
||||
- **Read-only fields** display pre-filled values that cannot be modified
|
||||
- **Number fields** validate against minimum and maximum values when configured
|
||||
- **Checkbox fields** can enforce a minimum or maximum number of selections
|
||||
|
||||
If validation fails, the recipient sees an error message and must correct the input before proceeding.
|
||||
|
||||
## Related
|
||||
|
||||
- [Add Fields to Documents](/docs/users/documents/add-fields) - Learn how to place fields on your documents
|
||||
- [Recipient Roles](/docs/concepts/recipient-roles) - Understand who can be assigned fields
|
||||
- [Fields API](/docs/developers/api/fields) - Programmatically add fields via the API
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: Foundational concepts behind document signing, recipient roles, field types, and certificates.
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Document Lifecycle"
|
||||
description="How documents move from draft to pending to completed or rejected."
|
||||
href="/docs/concepts/document-lifecycle"
|
||||
/>
|
||||
<Card
|
||||
title="Recipient Roles"
|
||||
description="Signers, approvers, viewers, assistants, and CC recipients."
|
||||
href="/docs/concepts/recipient-roles"
|
||||
/>
|
||||
<Card
|
||||
title="Field Types"
|
||||
description="Signatures, text, dates, checkboxes, dropdowns, and more."
|
||||
href="/docs/concepts/field-types"
|
||||
/>
|
||||
<Card
|
||||
title="Signing Workflow"
|
||||
description="The complete process from preparing a document to collecting signatures and sealing the final PDF."
|
||||
href="/docs/concepts/signing-workflow"
|
||||
/>
|
||||
<Card
|
||||
title="Signing Certificates"
|
||||
description="How documents are digitally signed and verified."
|
||||
href="/docs/concepts/signing-certificates"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## How These Concepts Apply
|
||||
|
||||
These concepts work consistently across all ways you interact with Documenso:
|
||||
|
||||
- **Web application**: When you create documents in the UI, you'll select recipient roles, add fields, and track documents through their lifecycle states.
|
||||
|
||||
- **API integration**: The same concepts map directly to API endpoints. Documents have status fields, recipients have role properties, and fields have type configurations.
|
||||
|
||||
- **Self-hosting**: The signing certificate concept becomes particularly relevant when you deploy your own instance and configure your own certificates for document signing.
|
||||
|
||||
Understanding these fundamentals will make the rest of the documentation easier to follow.
|
||||
|
||||
---
|
||||
|
||||
## Related Sections
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="User Guide"
|
||||
description="Apply these concepts when sending documents."
|
||||
href="/docs/users"
|
||||
/>
|
||||
<Card
|
||||
title="Developer Guide"
|
||||
description="Work with these concepts through the API."
|
||||
href="/docs/developers"
|
||||
/>
|
||||
<Card
|
||||
title="Compliance"
|
||||
description="How these concepts relate to legal standards."
|
||||
href="/docs/compliance"
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"title": "Concepts",
|
||||
"pages": [
|
||||
"document-lifecycle",
|
||||
"recipient-roles",
|
||||
"field-types",
|
||||
"signing-workflow",
|
||||
"signing-certificates"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: Recipient Roles
|
||||
description: Signers, approvers, viewers, assistants, and CC recipients.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Role Overview
|
||||
|
||||
| Role | Action Required | Can Sign | Description |
|
||||
| --------- | --------------- | -------- | ---------------------------------------- |
|
||||
| Signer | Yes | Yes | Must sign the document |
|
||||
| Approver | Yes | Optional | Must approve the document |
|
||||
| Viewer | Yes | No | Must view the document |
|
||||
| Assistant | Yes | No | Can pre-fill fields for other recipients |
|
||||
| CC | No | No | Receives a copy after completion |
|
||||
|
||||
## Role Details
|
||||
|
||||
<Tabs items={['Signer', 'Approver', 'Viewer', 'Assistant', 'CC']}>
|
||||
<Tab value="Signer">
|
||||
|
||||
Signers are the primary recipients of a document. They must complete all signature fields assigned to them before the document can be finalized.
|
||||
|
||||
**What they can do:**
|
||||
|
||||
- Sign signature fields assigned to them
|
||||
- Fill out any other fields assigned to them (text, date, checkbox, etc.)
|
||||
- Download the document after signing
|
||||
|
||||
**What they cannot do:**
|
||||
|
||||
- Sign on behalf of other recipients
|
||||
- Modify fields assigned to other recipients
|
||||
|
||||
**When to use this role:**
|
||||
|
||||
- Contracts requiring a legally binding signature
|
||||
- Agreements where the recipient must formally consent
|
||||
- Any document that requires a signature to be valid
|
||||
|
||||
</Tab>
|
||||
<Tab value="Approver">
|
||||
|
||||
Approvers must review and approve the document, but signing is optional. The document cannot be completed until all approvers have given their approval.
|
||||
|
||||
**What they can do:**
|
||||
|
||||
- Approve or reject the document
|
||||
- Optionally add a signature if signature fields are assigned
|
||||
- Fill out fields assigned to them
|
||||
- Download the document after approval
|
||||
|
||||
**What they cannot do:**
|
||||
|
||||
- Complete the document without explicitly approving it
|
||||
- Modify fields assigned to other recipients
|
||||
|
||||
**When to use this role:**
|
||||
|
||||
- Documents requiring manager or supervisor approval
|
||||
- Workflows where review is required before final signatures
|
||||
- Compliance processes requiring sign-off from multiple parties
|
||||
|
||||
</Tab>
|
||||
<Tab value="Viewer">
|
||||
|
||||
Viewers must acknowledge that they have viewed the document. They cannot add signatures but must confirm they have reviewed the content.
|
||||
|
||||
**What they can do:**
|
||||
|
||||
- View the complete document
|
||||
- Confirm they have viewed it
|
||||
- Download the document after viewing
|
||||
|
||||
**What they cannot do:**
|
||||
|
||||
- Sign the document
|
||||
- Fill out fields (no fields can be assigned to viewers)
|
||||
- Modify the document in any way
|
||||
|
||||
**When to use this role:**
|
||||
|
||||
- Informational documents that require acknowledgment
|
||||
- Policies or disclosures that recipients must review
|
||||
- Documents where you need proof of receipt without a signature
|
||||
|
||||
</Tab>
|
||||
<Tab value="Assistant">
|
||||
|
||||
Assistants can prepare the document by pre-filling fields on behalf of other signers. This role is only available when sequential signing is enabled.
|
||||
|
||||
**What they can do:**
|
||||
|
||||
- Pre-fill suggested values in fields assigned to later signers
|
||||
- Help prepare the document for the actual signers
|
||||
- Fill out any fields specifically assigned to them
|
||||
|
||||
**What they cannot do:**
|
||||
|
||||
- Sign on behalf of other recipients
|
||||
- Submit the document as complete
|
||||
- Be used in parallel signing mode
|
||||
|
||||
**When to use this role:**
|
||||
|
||||
- Administrative staff preparing documents for executives to sign
|
||||
- Workflows where one person gathers information and another signs
|
||||
- Situations where you want to reduce the burden on the final signer
|
||||
|
||||
<Callout type="info">
|
||||
The Assistant role requires sequential signing to be enabled. You cannot use this role when
|
||||
recipients sign in parallel.
|
||||
</Callout>
|
||||
|
||||
</Tab>
|
||||
<Tab value="CC">
|
||||
|
||||
CC recipients receive a copy of the completed document but do not need to take any action. They are notified when the document is fully signed.
|
||||
|
||||
**What they can do:**
|
||||
|
||||
- Receive a copy of the completed document
|
||||
- Download the signed document
|
||||
|
||||
**What they cannot do:**
|
||||
|
||||
- Sign or approve the document
|
||||
- View the document before it is completed
|
||||
- Take any action that affects document completion
|
||||
|
||||
**When to use this role:**
|
||||
|
||||
- Keeping stakeholders informed about signed agreements
|
||||
- Sending copies to legal or compliance teams
|
||||
- Archiving completed documents with relevant parties
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Signing Order
|
||||
|
||||
You can control the sequence in which recipients receive and act on a document by enabling signing order.
|
||||
|
||||
<Tabs items={['Parallel signing (default)', 'Sequential signing']}>
|
||||
<Tab value="Parallel signing (default)">
|
||||
|
||||
All recipients receive the document simultaneously and can act in any order. The document is completed when all required recipients have finished their actions.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Sequential signing">
|
||||
|
||||
Recipients receive the document one at a time, in the order you specify. Each recipient must complete their action before the next recipient is notified.
|
||||
|
||||
To enable sequential signing:
|
||||
|
||||
1. When adding recipients, check the "Enable signing order" option
|
||||
2. Assign an order number to each recipient
|
||||
3. Recipients with the same order number can act simultaneously
|
||||
4. The document proceeds to the next order number only when all recipients at the current level have completed their actions
|
||||
|
||||
<Callout type="info">Sequential signing is required if you want to use the Assistant role.</Callout>
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Related
|
||||
|
||||
- [Add Recipients](/users/documents/add-recipients) - How to add recipients to a document
|
||||
- [Field Types](/concepts/field-types) - Learn about the different field types you can assign to recipients
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: Signing Certificates
|
||||
description: Documenso digitally signs completed documents using X.509 certificates, providing cryptographic proof of authenticity and integrity.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## How Documenso Signs Documents
|
||||
|
||||
Documenso applies a digital signature to the PDF when all recipients complete their actions.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Create hash
|
||||
|
||||
Creates a cryptographic hash of the document content.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Sign the hash
|
||||
|
||||
Signs the hash using the certificate's private key.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Embed signature
|
||||
|
||||
Embeds the signature and certificate information into the PDF.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
The signature is applied at the platform level, not by individual signers. Each signer's actions (signature image, text, checkboxes) are recorded and sealed together in the final signed document.
|
||||
|
||||
## What the Signature Proves
|
||||
|
||||
The digital signature provides two guarantees:
|
||||
|
||||
| Guarantee | Description |
|
||||
| ---------------- | -------------------------------------------------------------------------- |
|
||||
| **Integrity** | The document has not been altered since signing |
|
||||
| **Authenticity** | The document was signed by the certificate holder (the Documenso instance) |
|
||||
|
||||
If anyone modifies the PDF after signing, the signature becomes invalid. PDF readers will display a warning that the document has been changed.
|
||||
|
||||
## Timestamps
|
||||
|
||||
Documenso can include a trusted timestamp from a Time Stamping Authority (TSA) in the signature. This proves when the document was signed, independent of the signer's system clock. Timestamps are important for:
|
||||
|
||||
- Legal evidence of when signing occurred
|
||||
- Long-term validation (LTV) of signatures
|
||||
- Compliance with archival requirements
|
||||
|
||||
## Viewing the Signature in PDF Readers
|
||||
|
||||
You can verify a signed document's signature in any PDF reader that supports digital signatures.
|
||||
|
||||
<Tabs items={['Adobe Acrobat', 'Other PDF readers']}>
|
||||
<Tab value="Adobe Acrobat">
|
||||
|
||||
1. Open the signed PDF
|
||||
2. Click the signature panel on the left, or click on a signature field
|
||||
3. View certificate details, signing time, and validation status
|
||||
|
||||
</Tab>
|
||||
<Tab value="Other PDF readers">
|
||||
|
||||
Preview, Foxit, and other PDF readers also display signature information, though the interface varies. Look for a signatures or security panel in the application menu.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
The signature panel shows who signed (certificate subject), when it was signed, whether the document has been modified, and certificate trust status.
|
||||
|
||||
## Certificate Trust and Validation
|
||||
|
||||
PDF readers validate signatures against their list of trusted Certificate Authorities (CAs). You may see different validation results depending on the certificate type:
|
||||
|
||||
| Certificate Type | Validation Result |
|
||||
| ---------------- | ---------------------------------------------------------------------- |
|
||||
| **CA-issued** | Green checkmark in Adobe if the CA is on the Adobe Approved Trust List |
|
||||
| **Self-signed** | Warning that the certificate is not from a trusted source |
|
||||
|
||||
<Callout type="info">
|
||||
A self-signed certificate still provides integrity verification. The document cannot be modified without invalidating the signature. The warning only indicates that a third-party CA has not verified the certificate issuer's identity.
|
||||
|
||||
For most use cases, self-signed certificates are sufficient. The signature still proves the document came from your Documenso instance and has not been tampered with.
|
||||
|
||||
</Callout>
|
||||
|
||||
## Using Custom Certificates
|
||||
|
||||
If you self-host Documenso, you can use your own signing certificate.
|
||||
|
||||
<Tabs items={['Self-signed', 'CA-issued']}>
|
||||
<Tab value="Self-signed">
|
||||
|
||||
Free and suitable for most use cases. The signature still proves document integrity and authenticity.
|
||||
|
||||
You may see a warning in PDF readers that the certificate is not from a trusted source, but the document cannot be modified without invalidating the signature.
|
||||
|
||||
</Tab>
|
||||
<Tab value="CA-issued">
|
||||
|
||||
Provides trusted validation in PDF readers (e.g. green checkmark in Adobe) when the CA is on the Adobe Approved Trust List.
|
||||
|
||||
Required for some compliance scenarios where third-party verification of the certificate issuer is needed.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
See [Signing Certificate Configuration](/docs/self-hosting/configuration/signing-certificate) for setup instructions.
|
||||
|
||||
## Related
|
||||
|
||||
- [Signature Levels](/docs/compliance/signature-levels) - Simple, Advanced, and Qualified electronic signatures
|
||||
- [Standards and Regulations](/docs/compliance/standards) - ESIGN, eIDAS, and other compliance frameworks
|
||||
- [Signing Certificate Configuration](/docs/self-hosting/configuration/signing-certificate) - Self-hosting certificate setup
|
||||
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: Signing Workflow
|
||||
description: The complete process from preparing a document to collecting signatures and sealing the final PDF.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Workflow Overview
|
||||
|
||||
A typical signing workflow follows these steps:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Prepare Document] --> B[Send Document] --> C[Notify Recipients] --> D[Recipients Sign] --> E[Seal & Finalize] --> F[Document Completed]
|
||||
```
|
||||
|
||||
1. **Prepare** - Upload the document, add recipients, and place fields
|
||||
2. **Send** - Distribute the document to recipients
|
||||
3. **Notify** - Recipients receive signing requests
|
||||
4. **Sign** - Recipients complete their assigned fields
|
||||
5. **Complete** - Document is sealed and distributed to all parties
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Prepare the document
|
||||
|
||||
Document preparation involves three main tasks: uploading, adding recipients, and placing fields.
|
||||
|
||||
**Upload the document**
|
||||
|
||||
Start by uploading a PDF. You can upload directly:
|
||||
|
||||
- from your device
|
||||
- create from an existing template
|
||||
- or duplicate a previously sent document.
|
||||
|
||||
Once uploaded, the document enters the **Draft** state.
|
||||
|
||||
**Add recipients**
|
||||
|
||||
Add the people who need to interact with the document. Each recipient needs:
|
||||
|
||||
- an email address
|
||||
- a name
|
||||
- a role
|
||||
|
||||
Available roles are:
|
||||
|
||||
| Role | Purpose |
|
||||
| --------- | --------------------------------------------------- |
|
||||
| Signer | Must sign the document |
|
||||
| Approver | Must approve (signature optional) |
|
||||
| Viewer | Must confirm they viewed the document |
|
||||
| Assistant | Pre-fills fields for other recipients |
|
||||
| CC | Receives a copy after completion (no action needed) |
|
||||
|
||||
**Place fields**
|
||||
|
||||
Add fields that recipients will complete. At minimum, each signer needs one signature field. You can also add:
|
||||
|
||||
- name
|
||||
- email
|
||||
- date
|
||||
- text
|
||||
- number
|
||||
- dropdown
|
||||
- checkbox
|
||||
- radio
|
||||
- initials fields
|
||||
|
||||
Each field is assigned to a specific recipient, indicated by color coding in the editor.
|
||||
|
||||
<Callout type="info">
|
||||
The document cannot be sent until every signer has at least one signature field assigned to them.
|
||||
</Callout>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Send the document
|
||||
|
||||
When the document is ready, you send it to recipients. You have two distribution options:
|
||||
|
||||
<Tabs items={['Email distribution', 'Manual distribution']}>
|
||||
<Tab value="Email distribution">
|
||||
|
||||
Recipients receive an email notification with a link to sign.
|
||||
|
||||
You can customize the email:
|
||||
|
||||
- subject line
|
||||
- message body with personalized variables
|
||||
- reply-to address for recipient responses
|
||||
|
||||
</Tab>
|
||||
<Tab value="Manual distribution">
|
||||
|
||||
Generate signing links without sending emails.
|
||||
|
||||
Use this when you want to:
|
||||
|
||||
- send links via SMS or messaging apps
|
||||
- embed links in your own application
|
||||
- control notification timing yourself
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
After sending, the document moves from **Draft** to **Pending** status.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Recipients are notified
|
||||
|
||||
When you send a document via email, each recipient receives a notification containing:
|
||||
|
||||
- the document title
|
||||
- your name and email (or team name)
|
||||
- your custom message (or a role-specific default)
|
||||
- a unique signing link
|
||||
|
||||
The signing link is specific to each recipient and cannot be used by others. Links remain active until the document is completed, deleted, or expired.
|
||||
|
||||
**Signing order**
|
||||
|
||||
By default, all recipients are notified simultaneously (parallel signing). If you enable sequential signing, only recipients in the first signing position receive notifications initially. When they complete their actions, the next group is notified.
|
||||
|
||||
This continues until all recipients have been notified and completed their actions.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Recipients sign
|
||||
|
||||
When a recipient clicks their signing link, they see the document with their assigned fields highlighted. The signing experience depends on their role:
|
||||
|
||||
<Tabs items={['Signer', 'Approver', 'Viewer']}>
|
||||
<Tab value="Signer">
|
||||
|
||||
Signers must complete all required fields before they can finish. For signature fields, they can:
|
||||
|
||||
- draw a signature using mouse or touchscreen
|
||||
- type their name and select a font style
|
||||
- upload an image of their existing signature
|
||||
|
||||
After completing all fields, the signer clicks a button to submit. They receive a confirmation and can download a copy of the document showing their completed fields.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Approver">
|
||||
|
||||
Approvers review the document and must explicitly approve it. If signature fields are assigned, they can optionally sign.
|
||||
|
||||
The document cannot proceed until all approvers have given approval.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Viewer">
|
||||
|
||||
Viewers see the full document and must confirm they have viewed it. They cannot add signatures or modify any content.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
**Authentication**
|
||||
|
||||
You can require recipients to verify their identity before signing through:
|
||||
|
||||
- email verification (confirm access to the email address)
|
||||
- access code (enter a code you provide separately)
|
||||
- passkey (authenticate with a hardware or software passkey)
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Document is completed
|
||||
|
||||
Once all recipients with required actions have completed them, the document is finalized.
|
||||
|
||||
| Aspect | Description |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Sealing** | The completed document is sealed with a digital certificate that cryptographically signs the PDF, prevents modification without detection, and provides proof of authenticity. |
|
||||
| **Audit trail** | An audit log is generated and can be attached to the document. It records when the document was created and sent, when each recipient viewed and signed, IP addresses and timestamps for each action, and any authentication methods used. |
|
||||
| **Distribution** | All parties receive the completed document: signers, approvers, and viewers receive their copy via email; CC recipients receive their first notification with the completed document; the document owner can download the signed PDF from their dashboard. |
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Workflow Variations
|
||||
|
||||
Documenso supports several workflow variations to handle different signing scenarios.
|
||||
|
||||
<Tabs items={['Sequential signing', 'Approval workflows', 'Assistants for pre-filling', 'Direct links']}>
|
||||
<Tab value="Sequential signing">
|
||||
|
||||
When recipients must sign in a specific order, enable signing order:
|
||||
|
||||
1. Assign each recipient a signing position (1, 2, 3, etc.)
|
||||
2. Recipients at position 1 sign first
|
||||
3. Recipients at position 2 are notified only after position 1 completes
|
||||
4. Multiple recipients can share the same position to sign in parallel within that step
|
||||
|
||||
Use sequential signing when later signers:
|
||||
|
||||
- need to see what earlier signers entered
|
||||
- approval must happen before final signatures
|
||||
- company policy requires a specific signing order
|
||||
|
||||
</Tab>
|
||||
<Tab value="Approval workflows">
|
||||
|
||||
Combine approver and signer roles to create approval workflows:
|
||||
|
||||
1. Add approvers at signing position 1
|
||||
2. Add signers at signing position 2
|
||||
3. Approvers review and approve first
|
||||
4. Signers are notified only after approval is complete
|
||||
|
||||
<Callout type="warn">
|
||||
If an approver rejects the document (when rejection is enabled), the workflow stops and signers
|
||||
are never notified.
|
||||
</Callout>
|
||||
|
||||
</Tab>
|
||||
<Tab value="Assistants for pre-filling">
|
||||
|
||||
Use assistants to have one person prepare the document for another:
|
||||
|
||||
1. Add an assistant at signing position 1
|
||||
2. Add the final signer at signing position 2
|
||||
3. The assistant pre-fills fields with suggested values
|
||||
4. The signer reviews and completes their signature
|
||||
|
||||
This is useful when administrative staff prepare documents for executives or when gathering information from one person while another signs.
|
||||
|
||||
<Callout type="info">
|
||||
The Assistant role is only available when sequential signing is enabled.
|
||||
</Callout>
|
||||
|
||||
</Tab>
|
||||
<Tab value="Direct links">
|
||||
|
||||
For high-volume signing scenarios, you can create direct links that allow anyone to sign without receiving an individual invitation:
|
||||
|
||||
- Generate a public signing link for a document or template
|
||||
- Share the link on your website, in emails, or through other channels
|
||||
- Each person who accesses the link creates their own signing instance
|
||||
- Useful for waivers, consent forms, and public agreements
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- [Document Lifecycle](/docs/concepts/document-lifecycle) - Understanding document states from draft to completion
|
||||
- [Recipient Roles](/docs/concepts/recipient-roles) - Detailed explanation of each role type
|
||||
- [Field Types](/docs/concepts/field-types) - All available field types and their configuration options
|
||||
- [Signing Certificates](/docs/concepts/signing-certificates) - How documents are digitally sealed
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: Developer Mode
|
||||
description: Advanced development tools for debugging field coordinates and integrating with the Documenso API.
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Developer mode provides additional tools and features to help you integrate and debug Documenso.
|
||||
|
||||
## Field Coordinates
|
||||
|
||||
Field coordinates represent the position of a field in a document. They are returned in the `pageX`, `pageY`, `width` and `height` properties of the field.
|
||||
|
||||
To enable field coordinates, add the `devmode=true` query parameter to the editor URL.
|
||||
|
||||
```bash
|
||||
# Legacy editor
|
||||
|
||||
https://app.documenso.com/t/<team-url>/documents/<envelope-id>/legacy_editor?devmode=true
|
||||
```
|
||||
|
||||

|
||||
|
||||
```bash
|
||||
# New editor
|
||||
|
||||
https://app.documenso.com/t/<team-url>/documents/<envelope-id>/edit?step=addFields&devmode=true
|
||||
```
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Fields API](/docs/developers/api/fields) - Create and position fields via API
|
||||
- [Field Types](/docs/concepts/field-types) - Detailed field type reference
|
||||
@@ -0,0 +1,815 @@
|
||||
---
|
||||
title: Documents API
|
||||
description: Create, manage, and send documents for signing via the API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
<Callout type="warn">
|
||||
This guide may not reflect the latest endpoints or parameters. For an always up-to-date reference,
|
||||
see the [OpenAPI Reference](https://openapi.documenso.com).
|
||||
</Callout>
|
||||
|
||||
## Overview
|
||||
|
||||
[Documents](/docs/users/documents) (called "envelopes" in the API) are the core resource in Documenso. You can:
|
||||
|
||||
1. create documents with recipients and fields
|
||||
2. send them for signing
|
||||
3. track their status
|
||||
4. retrieve the completed PDFs
|
||||
|
||||
Each document contains one or more PDF files, a list of recipients, and the fields they need to fill.
|
||||
|
||||
## Document Object
|
||||
|
||||
A document object contains the following properties:
|
||||
|
||||
| Property | Type | Description |
|
||||
| --------------- | -------------- | -------------------------------------------------------------- |
|
||||
| `id` | string | Unique identifier (e.g., `envelope_abc123`) |
|
||||
| `type` | string | `DOCUMENT` or `TEMPLATE` |
|
||||
| `status` | string | Current status: `DRAFT`, `PENDING`, `COMPLETED`, or `REJECTED` |
|
||||
| `title` | string | Document title |
|
||||
| `source` | string | How the document was created: `DOCUMENT`, `TEMPLATE`, `API` |
|
||||
| `visibility` | string | Who can view: `EVERYONE`, `ADMIN`, `MANAGER_AND_ABOVE` |
|
||||
| `externalId` | string \| null | Your custom identifier for the document |
|
||||
| `createdAt` | string | ISO 8601 timestamp |
|
||||
| `updatedAt` | string | ISO 8601 timestamp |
|
||||
| `completedAt` | string \| null | Timestamp when all recipients completed signing |
|
||||
| `deletedAt` | string \| null | Timestamp if soft-deleted |
|
||||
| `recipients` | array | List of recipients and their signing status |
|
||||
| `fields` | array | Signature and form fields on the document |
|
||||
| `envelopeItems` | array | PDF files attached to the document |
|
||||
| `documentMeta` | object | Email settings, redirect URL, signing options |
|
||||
|
||||
### Example Document Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "envelope_abc123xyz",
|
||||
"type": "DOCUMENT",
|
||||
"status": "PENDING",
|
||||
"source": "API",
|
||||
"visibility": "EVERYONE",
|
||||
"title": "Service Agreement",
|
||||
"externalId": "contract-2025-001",
|
||||
"createdAt": "2025-01-15T10:30:00.000Z",
|
||||
"updatedAt": "2025-01-15T10:35:00.000Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"recipients": [
|
||||
{
|
||||
"id": 1,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Smith",
|
||||
"role": "SIGNER",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"signingOrder": 1
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"id": "field_123",
|
||||
"type": "SIGNATURE",
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5,
|
||||
"recipientId": 1
|
||||
}
|
||||
],
|
||||
"envelopeItems": [
|
||||
{
|
||||
"id": "envelope_item_xyz",
|
||||
"title": "contract.pdf",
|
||||
"order": 1
|
||||
}
|
||||
],
|
||||
"documentMeta": {
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hi, please review and sign this agreement.",
|
||||
"timezone": "America/New_York",
|
||||
"redirectUrl": "https://example.com/thank-you"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## List Documents
|
||||
|
||||
Retrieve a paginated list of documents.
|
||||
|
||||
```
|
||||
GET /envelope
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ------------------ | ------- | ------------------------------------------------------------- |
|
||||
| `page` | integer | Page number (default: 1) |
|
||||
| `perPage` | integer | Results per page (default: 10, max: 100) |
|
||||
| `type` | string | Filter by `DOCUMENT` or `TEMPLATE` |
|
||||
| `status` | string | Filter by status: `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED` |
|
||||
| `source` | string | Filter by creation source |
|
||||
| `folderId` | string | Filter by folder ID |
|
||||
| `orderByColumn` | string | Sort field (only `createdAt` supported) |
|
||||
| `orderByDirection` | string | Sort direction: `asc` or `desc` (default: `desc`) |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
# List all documents
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
|
||||
# Filter by status and paginate
|
||||
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope?status=PENDING&page=1&perPage=20" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
|
||||
# List only documents (not templates)
|
||||
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope?type=DOCUMENT" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
|
||||
````
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const API_TOKEN = process.env.DOCUMENSO_API_TOKEN;
|
||||
const BASE_URL = 'https://app.documenso.com/api/v2';
|
||||
|
||||
// List all documents
|
||||
const response = await fetch(`${BASE_URL}/envelope`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
},
|
||||
});
|
||||
|
||||
const { data, pagination } = await response.json();
|
||||
console.log(`Found ${pagination.totalItems} documents`);
|
||||
|
||||
// Filter by status
|
||||
const pendingResponse = await fetch(
|
||||
`${BASE_URL}/envelope?status=PENDING&page=1&perPage=20`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const pendingDocs = await pendingResponse.json();
|
||||
````
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "envelope_abc123",
|
||||
"type": "DOCUMENT",
|
||||
"status": "PENDING",
|
||||
"title": "Service Agreement",
|
||||
"createdAt": "2025-01-15T10:30:00.000Z",
|
||||
"updatedAt": "2025-01-15T10:35:00.000Z",
|
||||
"recipients": [
|
||||
{
|
||||
"id": 1,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Smith",
|
||||
"role": "SIGNER",
|
||||
"signingStatus": "NOT_SIGNED"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"perPage": 10,
|
||||
"totalPages": 5,
|
||||
"totalItems": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Get Document
|
||||
|
||||
Retrieve a single document by ID.
|
||||
|
||||
```
|
||||
GET /envelope/{envelopeId}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ------------ | ------ | ----------------------------------------- |
|
||||
| `envelopeId` | string | The document ID (e.g., `envelope_abc123`) |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope/envelope_abc123" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const envelopeId = 'envelope_abc123';
|
||||
|
||||
const response = await fetch(`https://app.documenso.com/api/v2/envelope/${envelopeId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
},
|
||||
});
|
||||
|
||||
const document = await response.json();
|
||||
console.log(document.title, document.status);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
Returns the full document object including recipients, fields, and envelope items.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "envelope_abc123",
|
||||
"type": "DOCUMENT",
|
||||
"status": "PENDING",
|
||||
"title": "Service Agreement",
|
||||
"recipients": [...],
|
||||
"fields": [...],
|
||||
"envelopeItems": [...],
|
||||
"documentMeta": {...}
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Create Document
|
||||
|
||||
Create a new document with optional recipients and fields in a single request.
|
||||
|
||||
```
|
||||
POST /envelope/create
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
The request uses `multipart/form-data` with two parts:
|
||||
|
||||
| Part | Type | Description |
|
||||
| --------- | ------- | ---------------------- |
|
||||
| `payload` | JSON | Document configuration |
|
||||
| `files` | File(s) | One or more PDF files |
|
||||
|
||||
### Payload Schema
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | ------------------------------------------- |
|
||||
| `type` | string | Yes | Must be `DOCUMENT` |
|
||||
| `title` | string | Yes | Document title |
|
||||
| `externalId` | string | No | Your custom identifier |
|
||||
| `visibility` | string | No | `EVERYONE`, `ADMIN`, or `MANAGER_AND_ABOVE` |
|
||||
| `folderId` | string | No | Folder ID to create the document in |
|
||||
| `recipients` | array | No | Recipients with optional fields |
|
||||
| `meta` | object | No | Email subject, message, redirect URL, etc. |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F 'payload={
|
||||
"type": "DOCUMENT",
|
||||
"title": "Service Agreement",
|
||||
"externalId": "contract-2025-001",
|
||||
"recipients": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"name": "John Smith",
|
||||
"role": "SIGNER",
|
||||
"fields": [
|
||||
{
|
||||
"identifier": 0,
|
||||
"type": "SIGNATURE",
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5
|
||||
},
|
||||
{
|
||||
"identifier": 0,
|
||||
"type": "DATE",
|
||||
"page": 1,
|
||||
"positionX": 50,
|
||||
"positionY": 80,
|
||||
"width": 20,
|
||||
"height": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"subject": "Please sign this agreement",
|
||||
"message": "Hi John, please review and sign the attached agreement.",
|
||||
"redirectUrl": "https://example.com/thank-you"
|
||||
}
|
||||
}' \
|
||||
-F "files=@./contract.pdf;type=application/pdf"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
import fs from 'fs';
|
||||
import FormData from 'form-data';
|
||||
|
||||
const form = new FormData();
|
||||
|
||||
const payload = {
|
||||
type: 'DOCUMENT',
|
||||
title: 'Service Agreement',
|
||||
externalId: 'contract-2025-001',
|
||||
recipients: [
|
||||
{
|
||||
email: 'signer@example.com',
|
||||
name: 'John Smith',
|
||||
role: 'SIGNER',
|
||||
fields: [
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'SIGNATURE',
|
||||
page: 1,
|
||||
positionX: 10,
|
||||
positionY: 80,
|
||||
width: 30,
|
||||
height: 5,
|
||||
},
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'DATE',
|
||||
page: 1,
|
||||
positionX: 50,
|
||||
positionY: 80,
|
||||
width: 20,
|
||||
height: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
subject: 'Please sign this agreement',
|
||||
message: 'Hi John, please review and sign the attached agreement.',
|
||||
redirectUrl: 'https://example.com/thank-you',
|
||||
},
|
||||
};
|
||||
|
||||
form.append('payload', JSON.stringify(payload));
|
||||
form.append('files', fs.createReadStream('./contract.pdf'), {
|
||||
contentType: 'application/pdf',
|
||||
});
|
||||
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
const { id } = await response.json();
|
||||
console.log('Created document:', id);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "envelope_abc123xyz"
|
||||
}
|
||||
````
|
||||
|
||||
### Field Positioning
|
||||
|
||||
Field positions use percentage values (0-100) relative to the PDF page:
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------ | ---------------------------------------------------------- |
|
||||
| `positionX` | Horizontal position from left edge (0 = left, 100 = right) |
|
||||
| `positionY` | Vertical position from top edge (0 = top, 100 = bottom) |
|
||||
| `width` | Field width as percentage of page width |
|
||||
| `height` | Field height as percentage of page height |
|
||||
| `page` | Page number (1-indexed) |
|
||||
| `identifier` | File index (0 for first file) or filename |
|
||||
|
||||
### Field Types
|
||||
|
||||
| Type | Description |
|
||||
| ----------- | --------------------------- |
|
||||
| `SIGNATURE` | Signature field |
|
||||
| `INITIALS` | Initials field |
|
||||
| `NAME` | Auto-filled recipient name |
|
||||
| `EMAIL` | Auto-filled recipient email |
|
||||
| `DATE` | Signing date |
|
||||
| `TEXT` | Free text input |
|
||||
| `NUMBER` | Numeric input |
|
||||
| `CHECKBOX` | Checkbox selection |
|
||||
| `RADIO` | Radio button group |
|
||||
| `DROPDOWN` | Dropdown selection |
|
||||
|
||||
### Recipient Roles
|
||||
|
||||
| Role | Description |
|
||||
| ---------- | ----------------------------------------- |
|
||||
| `SIGNER` | Must sign the document |
|
||||
| `APPROVER` | Must approve before signers can sign |
|
||||
| `CC` | Receives a copy but doesn't sign |
|
||||
| `VIEWER` | Can view the document but takes no action |
|
||||
|
||||
---
|
||||
|
||||
## Update Document
|
||||
|
||||
Update a document's properties. Only works on documents in `DRAFT` status.
|
||||
|
||||
```
|
||||
POST /envelope/update
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | ------------------------------------ |
|
||||
| `envelopeId` | string | Yes | Document ID |
|
||||
| `data` | object | No | Document properties to update |
|
||||
| `meta` | object | No | Email and signing settings to update |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/update" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "envelope_abc123",
|
||||
"data": {
|
||||
"title": "Updated Service Agreement",
|
||||
"externalId": "contract-2025-001-v2"
|
||||
},
|
||||
"meta": {
|
||||
"subject": "Updated: Please sign this agreement",
|
||||
"redirectUrl": "https://example.com/signed"
|
||||
}
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/update', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'envelope_abc123',
|
||||
data: {
|
||||
title: 'Updated Service Agreement',
|
||||
externalId: 'contract-2025-001-v2',
|
||||
},
|
||||
meta: {
|
||||
subject: 'Updated: Please sign this agreement',
|
||||
redirectUrl: 'https://example.com/signed',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const document = await response.json();
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Send Document
|
||||
|
||||
Send a document to recipients for signing. This changes the status from `DRAFT` to `PENDING`.
|
||||
|
||||
```
|
||||
|
||||
POST /envelope/distribute
|
||||
|
||||
````
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `envelopeId` | string | Yes | Document ID |
|
||||
| `meta` | object | No | Override email settings for this send |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
# Basic send
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/distribute" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "envelope_abc123"
|
||||
}'
|
||||
|
||||
# Send with custom email settings
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/distribute" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "envelope_abc123",
|
||||
"meta": {
|
||||
"subject": "Action Required: Sign Agreement",
|
||||
"message": "Please sign this document by end of day.",
|
||||
"timezone": "America/New_York"
|
||||
}
|
||||
}'
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/distribute', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'envelope_abc123',
|
||||
meta: {
|
||||
subject: 'Action Required: Sign Agreement',
|
||||
message: 'Please sign this document by end of day.',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { id, recipients } = await response.json();
|
||||
|
||||
// Recipients now include signing URLs
|
||||
recipients.forEach((r) => {
|
||||
console.log(`${r.email}: ${r.signingUrl}`);
|
||||
});
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
The response includes signing URLs for each recipient:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"id": "envelope_abc123",
|
||||
"recipients": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "John Smith",
|
||||
"email": "signer@example.com",
|
||||
"token": "abc123xyz",
|
||||
"role": "SIGNER",
|
||||
"signingOrder": 1,
|
||||
"signingUrl": "https://app.documenso.com/sign/abc123xyz"
|
||||
}
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
<Callout type="info">
|
||||
Use the `signingUrl` to redirect recipients directly to the signing page, or let them use the
|
||||
email link.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Delete Document
|
||||
|
||||
Delete a document. Completed documents cannot be deleted.
|
||||
|
||||
```
|
||||
POST /envelope/delete
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | ----------- |
|
||||
| `envelopeId` | string | Yes | Document ID |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/delete" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "envelope_abc123"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/delete', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'envelope_abc123',
|
||||
}),
|
||||
});
|
||||
|
||||
const { success } = await response.json();
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Get Multiple Documents
|
||||
|
||||
Retrieve multiple documents by their IDs in a single request.
|
||||
|
||||
```
|
||||
POST /envelope/get-many
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------- | ----- | -------- | --------------------- |
|
||||
| `envelopeIds` | array | Yes | Array of document IDs |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/get-many" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeIds": ["envelope_abc123", "envelope_def456", "envelope_ghi789"]
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/get-many', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeIds: ['envelope_abc123', 'envelope_def456', 'envelope_ghi789'],
|
||||
}),
|
||||
});
|
||||
|
||||
const documents = await response.json();
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Document Statuses
|
||||
|
||||
| Status | Description |
|
||||
| --- | --- |
|
||||
| `DRAFT` | Document is being prepared. Recipients have not been notified. |
|
||||
| `PENDING` | Document has been sent. Waiting for recipients to sign. |
|
||||
| `COMPLETED` | All recipients have signed. Document is sealed. |
|
||||
| `REJECTED` | A recipient rejected the document. |
|
||||
|
||||
### Status Transitions
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DRAFT --> PENDING --> COMPLETED
|
||||
PENDING --> REJECTED
|
||||
```
|
||||
|
||||
- **DRAFT to PENDING**: Call the distribute endpoint
|
||||
- **PENDING to COMPLETED**: All recipients complete their signing
|
||||
- **PENDING to REJECTED**: A recipient rejects the document
|
||||
|
||||
<Callout type="warn">
|
||||
You cannot modify recipients or fields after a document moves to `PENDING` status.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Filtering and Pagination
|
||||
|
||||
### Pagination Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| --------- | ------- | ------- | --------------------------- |
|
||||
| `page` | integer | 1 | Page number |
|
||||
| `perPage` | integer | 10 | Results per page (max: 100) |
|
||||
|
||||
### Filter Parameters
|
||||
|
||||
| Parameter | Values | Description |
|
||||
| ---------- | ------------------------------------------- | ------------------------- |
|
||||
| `type` | `DOCUMENT`, `TEMPLATE` | Filter by envelope type |
|
||||
| `status` | `DRAFT`, `PENDING`, `COMPLETED`, `REJECTED` | Filter by status |
|
||||
| `source` | `DOCUMENT`, `TEMPLATE`, `API` | Filter by creation source |
|
||||
| `folderId` | string | Filter by folder |
|
||||
|
||||
### Sorting
|
||||
|
||||
| Parameter | Values | Description |
|
||||
| ------------------ | ------------- | -------------------------------- |
|
||||
| `orderByColumn` | `createdAt` | Field to sort by |
|
||||
| `orderByDirection` | `asc`, `desc` | Sort direction (default: `desc`) |
|
||||
|
||||
### Example: Fetch All Pending Documents
|
||||
|
||||
```typescript
|
||||
async function getAllPendingDocuments() {
|
||||
const documents = [];
|
||||
let page = 1;
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const response = await fetch(
|
||||
`https://app.documenso.com/api/v2/envelope?status=PENDING&page=${page}&perPage=100`,
|
||||
{
|
||||
headers: { Authorization: 'api_xxxxxxxxxxxxxxxx' },
|
||||
},
|
||||
);
|
||||
|
||||
const { data, pagination } = await response.json();
|
||||
documents.push(...data);
|
||||
|
||||
hasMore = page < pagination.totalPages;
|
||||
page++;
|
||||
}
|
||||
|
||||
return documents;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Recipients API](/docs/developers/api/recipients) - Add and manage document recipients
|
||||
- [Fields API](/docs/developers/api/fields) - Add signature and form fields
|
||||
- [Templates API](/docs/developers/api/templates) - Create reusable document templates
|
||||
- [Webhooks](/docs/developers/webhooks) - Get notified when documents are signed
|
||||
@@ -0,0 +1,738 @@
|
||||
---
|
||||
title: Fields API
|
||||
description: Add signature and form fields to documents via API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
<Callout type="warn">
|
||||
This guide may not reflect the latest endpoints or parameters. For an always up-to-date reference,
|
||||
see the [OpenAPI Reference](https://openapi.documenso.com).
|
||||
</Callout>
|
||||
|
||||
## Field Object
|
||||
|
||||
| Property | Type | Description |
|
||||
| ---------------- | -------------- | -------------------------------------------- |
|
||||
| `id` | number | Unique field identifier |
|
||||
| `secondaryId` | string | Secondary identifier for audit logs |
|
||||
| `type` | string | Field type (see [Field Types](#field-types)) |
|
||||
| `recipientId` | number | ID of the recipient assigned to this field |
|
||||
| `envelopeId` | number | ID of the parent envelope |
|
||||
| `envelopeItemId` | string | ID of the PDF item the field is placed on |
|
||||
| `page` | number | Page number (1-indexed) |
|
||||
| `positionX` | number | X coordinate as percentage (0-100) |
|
||||
| `positionY` | number | Y coordinate as percentage (0-100) |
|
||||
| `width` | number | Width as percentage of page (0-100) |
|
||||
| `height` | number | Height as percentage of page (0-100) |
|
||||
| `customText` | string | Value entered by the recipient |
|
||||
| `inserted` | boolean | Whether the field has been completed |
|
||||
| `fieldMeta` | object \| null | Type-specific configuration options |
|
||||
|
||||
### Example Field Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 456,
|
||||
"secondaryId": "field_abc123",
|
||||
"type": "SIGNATURE",
|
||||
"recipientId": 123,
|
||||
"envelopeId": 789,
|
||||
"envelopeItemId": "envelope_item_xyz",
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5,
|
||||
"customText": "",
|
||||
"inserted": false,
|
||||
"fieldMeta": {
|
||||
"type": "signature",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Field Types
|
||||
|
||||
| Type | Description | Auto-filled |
|
||||
| ---------------- | ----------------------------------------- | ----------- |
|
||||
| `SIGNATURE` | Drawn, typed, or uploaded signature | No |
|
||||
| `FREE_SIGNATURE` | Unrestricted signature without validation | No |
|
||||
| `INITIALS` | Recipient's initials | No |
|
||||
| `NAME` | Recipient's full name | Yes |
|
||||
| `EMAIL` | Recipient's email address | Yes |
|
||||
| `DATE` | Date the field was completed | Yes |
|
||||
| `TEXT` | Free-form text input | No |
|
||||
| `NUMBER` | Numeric input with optional validation | No |
|
||||
| `RADIO` | Single selection from options | No |
|
||||
| `CHECKBOX` | Multiple selections from options | No |
|
||||
| `DROPDOWN` | Single selection from a dropdown menu | No |
|
||||
|
||||
---
|
||||
|
||||
## Get Field
|
||||
|
||||
Retrieve a single field by ID.
|
||||
|
||||
```
|
||||
GET /envelope/field/{fieldId}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ------ | ------------ |
|
||||
| `fieldId` | number | The field ID |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope/field/456" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/field/456',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const field = await response.json();
|
||||
console.log(field.type, field.page);
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
Returns the field object.
|
||||
|
||||
---
|
||||
|
||||
## Create Fields
|
||||
|
||||
Add one or more fields to a document.
|
||||
|
||||
```
|
||||
|
||||
POST /envelope/field/create-many
|
||||
|
||||
````
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ----------- | ------ | -------- | ------------------------------- |
|
||||
| `documentId`| number | Yes | The document ID |
|
||||
| `fields` | array | Yes | Array of field configurations |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/field/create-many" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"documentId": 123,
|
||||
"fields": [
|
||||
{
|
||||
"type": "SIGNATURE",
|
||||
"recipientId": 456,
|
||||
"pageNumber": 1,
|
||||
"pageX": 10,
|
||||
"pageY": 80,
|
||||
"width": 30,
|
||||
"height": 5
|
||||
},
|
||||
{
|
||||
"type": "DATE",
|
||||
"recipientId": 456,
|
||||
"pageNumber": 1,
|
||||
"pageX": 50,
|
||||
"pageY": 80,
|
||||
"width": 20,
|
||||
"height": 3
|
||||
},
|
||||
{
|
||||
"type": "TEXT",
|
||||
"recipientId": 456,
|
||||
"pageNumber": 1,
|
||||
"pageX": 10,
|
||||
"pageY": 70,
|
||||
"width": 40,
|
||||
"height": 4,
|
||||
"fieldMeta": {
|
||||
"type": "text",
|
||||
"label": "Job Title",
|
||||
"placeholder": "Enter your job title",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/field/create-many',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
documentId: 123,
|
||||
fields: [
|
||||
{
|
||||
type: 'SIGNATURE',
|
||||
recipientId: 456,
|
||||
pageNumber: 1,
|
||||
pageX: 10,
|
||||
pageY: 80,
|
||||
width: 30,
|
||||
height: 5,
|
||||
},
|
||||
{
|
||||
type: 'DATE',
|
||||
recipientId: 456,
|
||||
pageNumber: 1,
|
||||
pageX: 50,
|
||||
pageY: 80,
|
||||
width: 20,
|
||||
height: 3,
|
||||
},
|
||||
{
|
||||
type: 'TEXT',
|
||||
recipientId: 456,
|
||||
pageNumber: 1,
|
||||
pageX: 10,
|
||||
pageY: 70,
|
||||
width: 40,
|
||||
height: 4,
|
||||
fieldMeta: {
|
||||
type: 'text',
|
||||
label: 'Job Title',
|
||||
placeholder: 'Enter your job title',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const { fields } = await response.json();
|
||||
console.log(`Created ${fields.length} fields`);
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"id": 101,
|
||||
"type": "SIGNATURE",
|
||||
"recipientId": 456,
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"type": "DATE",
|
||||
"recipientId": 456,
|
||||
"page": 1,
|
||||
"positionX": 50,
|
||||
"positionY": 80,
|
||||
"width": 20,
|
||||
"height": 3
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"type": "TEXT",
|
||||
"recipientId": 456,
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 70,
|
||||
"width": 40,
|
||||
"height": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Update Fields
|
||||
|
||||
Update one or more fields in a single request.
|
||||
|
||||
```
|
||||
POST /envelope/field/update-many
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | ----------------------------- |
|
||||
| `documentId` | number | Yes | The document ID |
|
||||
| `fields` | array | Yes | Array of field update objects |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/field/update-many" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"documentId": 123,
|
||||
"fields": [
|
||||
{
|
||||
"id": 101,
|
||||
"type": "SIGNATURE",
|
||||
"pageY": 85
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"type": "DATE",
|
||||
"pageY": 85
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/field/update-many',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
documentId: 123,
|
||||
fields: [
|
||||
{ id: 101, type: 'SIGNATURE', pageY: 85 },
|
||||
{ id: 102, type: 'DATE', pageY: 85 },
|
||||
],
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const { fields } = await response.json();
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"fields": [
|
||||
{ "id": 101, "type": "SIGNATURE", "positionY": 85 },
|
||||
{ "id": 102, "type": "DATE", "positionY": 85 }
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Delete Field
|
||||
|
||||
Remove a field from a document.
|
||||
|
||||
```
|
||||
POST /envelope/field/delete
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| --------- | ------ | -------- | ------------ |
|
||||
| `fieldId` | number | Yes | The field ID |
|
||||
|
||||
### Code Examples
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/field/delete" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"fieldId": 456
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/field/delete',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fieldId: 456,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const { success } = await response.json();
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Field Positioning
|
||||
|
||||
Fields use percentage-based coordinates relative to the PDF page dimensions.
|
||||
|
||||
| Property | Range | Description |
|
||||
| ----------- | ----- | -------------------------------------------------- |
|
||||
| `positionX` | 0-100 | Horizontal position from left edge (0 = left edge) |
|
||||
| `positionY` | 0-100 | Vertical position from top edge (0 = top edge) |
|
||||
| `width` | 0-100 | Field width as percentage of page width |
|
||||
| `height` | 0-100 | Field height as percentage of page height |
|
||||
| `page` | 1+ | Page number (1-indexed) |
|
||||
|
||||
### Coordinate System
|
||||
|
||||
```
|
||||
(0,0) ─────────────────────────── (100,0)
|
||||
│ │
|
||||
│ ┌─────────┐ │
|
||||
│ │ Field │ (pageX: 10, │
|
||||
│ │ │ pageY: 20, │
|
||||
│ └─────────┘ width: 30, │
|
||||
│ height: 5) │
|
||||
│ │
|
||||
(0,100) ─────────────────────────(100,100)
|
||||
```
|
||||
|
||||
### Example: Position a Signature at Bottom Right
|
||||
|
||||
```typescript
|
||||
const field = {
|
||||
type: 'SIGNATURE',
|
||||
recipientId: 123,
|
||||
pageNumber: 1,
|
||||
pageX: 60, // 60% from left
|
||||
pageY: 85, // 85% from top (near bottom)
|
||||
width: 30, // 30% of page width
|
||||
height: 8, // 8% of page height
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Placeholder-Based Field Positioning
|
||||
|
||||
Instead of specifying exact coordinates, you can position fields using placeholder text embedded in your PDF. Include placeholder markers such as `{{signature, r1}}` in your document, and Documenso will create fields at those locations when the document is uploaded.
|
||||
|
||||
This approach is useful when generating PDFs programmatically or using templates with consistent layouts.
|
||||
|
||||
See the [PDF Placeholders](/docs/users/documents/advanced/pdf-placeholders) guide for the full placeholder format reference, including supported field types, recipient identifiers, and field options.
|
||||
|
||||
---
|
||||
|
||||
## Field Meta Options
|
||||
|
||||
Each field type supports specific configuration through `fieldMeta`.
|
||||
|
||||
### Common Options
|
||||
|
||||
All field types support these base options:
|
||||
|
||||
| Option | Type | Description |
|
||||
| ------------- | ------- | --------------------------------------- |
|
||||
| `label` | string | Display text shown near the field |
|
||||
| `placeholder` | string | Hint text when field is empty |
|
||||
| `required` | boolean | Whether field must be completed |
|
||||
| `readOnly` | boolean | Lock field with a pre-filled value |
|
||||
| `fontSize` | number | Text size in pixels (8-96, default: 12) |
|
||||
|
||||
### Signature Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "SIGNATURE",
|
||||
"fieldMeta": {
|
||||
"type": "signature",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Text Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TEXT",
|
||||
"fieldMeta": {
|
||||
"type": "text",
|
||||
"label": "Company Name",
|
||||
"placeholder": "Enter company name",
|
||||
"text": "Default value",
|
||||
"characterLimit": 100,
|
||||
"textAlign": "left",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
| ---------------- | ------ | ---------------------------------- |
|
||||
| `text` | string | Default value |
|
||||
| `characterLimit` | number | Maximum characters allowed |
|
||||
| `textAlign` | string | `left`, `center`, or `right` |
|
||||
| `lineHeight` | number | Spacing between lines (1-10) |
|
||||
| `letterSpacing` | number | Spacing between characters (0-100) |
|
||||
|
||||
### Number Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "NUMBER",
|
||||
"fieldMeta": {
|
||||
"type": "number",
|
||||
"label": "Quantity",
|
||||
"minValue": 1,
|
||||
"maxValue": 100,
|
||||
"value": "10",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
| -------------- | ------ | --------------------- |
|
||||
| `value` | string | Default value |
|
||||
| `minValue` | number | Minimum allowed value |
|
||||
| `maxValue` | number | Maximum allowed value |
|
||||
| `numberFormat` | string | Display format |
|
||||
|
||||
### Date Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "DATE",
|
||||
"fieldMeta": {
|
||||
"type": "date",
|
||||
"textAlign": "left",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Checkbox Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "CHECKBOX",
|
||||
"fieldMeta": {
|
||||
"type": "checkbox",
|
||||
"label": "Agreements",
|
||||
"values": [
|
||||
{ "id": 1, "value": "Terms of Service", "checked": false },
|
||||
{ "id": 2, "value": "Privacy Policy", "checked": false }
|
||||
],
|
||||
"validationRule": "min",
|
||||
"validationLength": 1,
|
||||
"direction": "vertical",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
| ------------------ | ------ | --------------------------------- |
|
||||
| `values` | array | List of checkbox options |
|
||||
| `validationRule` | string | Validation type for selections |
|
||||
| `validationLength` | number | Number for validation rule |
|
||||
| `direction` | string | `vertical` or `horizontal` layout |
|
||||
|
||||
### Radio Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "RADIO",
|
||||
"fieldMeta": {
|
||||
"type": "radio",
|
||||
"label": "Payment Method",
|
||||
"values": [
|
||||
{ "id": 1, "value": "Credit Card", "checked": false },
|
||||
{ "id": 2, "value": "Bank Transfer", "checked": true },
|
||||
{ "id": 3, "value": "Check", "checked": false }
|
||||
],
|
||||
"direction": "vertical",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dropdown Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "DROPDOWN",
|
||||
"fieldMeta": {
|
||||
"type": "dropdown",
|
||||
"label": "Country",
|
||||
"values": [{ "value": "United States" }, { "value": "Canada" }, { "value": "United Kingdom" }],
|
||||
"defaultValue": "United States",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
| -------------- | ------ | ------------------------ |
|
||||
| `values` | array | List of dropdown options |
|
||||
| `defaultValue` | string | Pre-selected option |
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
Create a document with a signature block containing multiple field types:
|
||||
|
||||
```typescript
|
||||
async function addSignatureBlock(documentId: number, recipientId: number) {
|
||||
const fields = [
|
||||
// Signature
|
||||
{
|
||||
type: 'SIGNATURE',
|
||||
recipientId,
|
||||
pageNumber: 1,
|
||||
pageX: 10,
|
||||
pageY: 80,
|
||||
width: 30,
|
||||
height: 8,
|
||||
fieldMeta: {
|
||||
type: 'signature',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
// Printed name
|
||||
{
|
||||
type: 'NAME',
|
||||
recipientId,
|
||||
pageNumber: 1,
|
||||
pageX: 10,
|
||||
pageY: 90,
|
||||
width: 30,
|
||||
height: 4,
|
||||
fieldMeta: {
|
||||
type: 'name',
|
||||
label: 'Printed Name',
|
||||
},
|
||||
},
|
||||
// Date
|
||||
{
|
||||
type: 'DATE',
|
||||
recipientId,
|
||||
pageNumber: 1,
|
||||
pageX: 50,
|
||||
pageY: 80,
|
||||
width: 20,
|
||||
height: 4,
|
||||
fieldMeta: {
|
||||
type: 'date',
|
||||
label: 'Date',
|
||||
},
|
||||
},
|
||||
// Job title
|
||||
{
|
||||
type: 'TEXT',
|
||||
recipientId,
|
||||
pageNumber: 1,
|
||||
pageX: 50,
|
||||
pageY: 90,
|
||||
width: 30,
|
||||
height: 4,
|
||||
fieldMeta: {
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
placeholder: 'Enter your job title',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/field/create-many', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ documentId, fields }),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Status | Description |
|
||||
| ------ | ---------------------------------------------------- |
|
||||
| `400` | Invalid field configuration or document already sent |
|
||||
| `401` | Invalid or missing API key |
|
||||
| `404` | Document, recipient, or field not found |
|
||||
| `500` | Server error |
|
||||
|
||||
<Callout type="warn">
|
||||
Fields cannot be modified after a document is sent for signing. Make all field changes while the
|
||||
document is in `DRAFT` status.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Documents API](/docs/developers/api/documents) - Create and manage documents
|
||||
- [Recipients API](/docs/developers/api/recipients) - Add signers to documents
|
||||
- [Field Types](/docs/concepts/field-types) - Detailed field type reference
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: API Reference
|
||||
description: Complete reference for the Documenso REST API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
<Callout type="warn">
|
||||
The guides below cover common API patterns but may not reflect the latest endpoints or parameters.
|
||||
For an always up-to-date reference, see the [OpenAPI Reference](https://openapi.documenso.com).
|
||||
</Callout>
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://app.documenso.com/api/v2
|
||||
```
|
||||
|
||||
For self-hosted instances, replace with your instance URL.
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require an API key in the `Authorization` header:
|
||||
|
||||
```
|
||||
Authorization: api_xxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
See [Authentication](/docs/developers/getting-started/authentication) for details.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Documents"
|
||||
description="Create, retrieve, update, and delete documents."
|
||||
href="/docs/developers/api/documents"
|
||||
/>
|
||||
<Card
|
||||
title="Recipients"
|
||||
description="Manage document recipients and signers."
|
||||
href="/docs/developers/api/recipients"
|
||||
/>
|
||||
<Card
|
||||
title="Fields"
|
||||
description="Add and configure signature fields."
|
||||
href="/docs/developers/api/fields"
|
||||
/>
|
||||
<Card
|
||||
title="Templates"
|
||||
description="Work with document templates."
|
||||
href="/docs/developers/api/templates"
|
||||
/>
|
||||
<Card
|
||||
title="Teams"
|
||||
description="Manage teams and team members."
|
||||
href="/docs/developers/api/teams"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [First API Call](/docs/developers/getting-started/first-api-call) - Quick start example
|
||||
- [Webhooks](/docs/developers/webhooks) - Get notified about document events
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "API Reference",
|
||||
"pages": [
|
||||
"documents",
|
||||
"recipients",
|
||||
"fields",
|
||||
"templates",
|
||||
"teams",
|
||||
"rate-limits",
|
||||
"versioning",
|
||||
"developer-mode"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Rate Limits
|
||||
description: Learn about the rate limits for the Documenso Public API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## Overview
|
||||
|
||||
Documenso enforces rate limits on all API endpoints to ensure service stability.
|
||||
|
||||
## HTTP Rate Limits
|
||||
|
||||
**Limit:** 100 requests per minute per IP address
|
||||
**Response:** 429 Too Many Requests
|
||||
|
||||
### Rate Limit Response
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Too many requests, please try again later."
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
No rate limit headers are currently provided. When you receive a 429 response, wait at least 60
|
||||
seconds before retrying.
|
||||
</Callout>
|
||||
|
||||
## Resource Limits
|
||||
|
||||
Beyond HTTP rate limits, your account has usage limits based on your subscription plan.
|
||||
|
||||
### Plan Limits
|
||||
|
||||
| Resource | Free | Paid | Self-hosted | Enterprise |
|
||||
| ---------------- | ---- | --------- | ----------- | ---------- |
|
||||
| Documents/month | 5 | Unlimited | Unlimited | Unlimited |
|
||||
| Total Recipients | 10 | Unlimited | Unlimited | Unlimited |
|
||||
| Direct Templates | 3 | Unlimited | Unlimited | Unlimited |
|
||||
|
||||
### Error Response
|
||||
|
||||
When you exceed a resource limit:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "You have reached your document limit for this month. Please upgrade your plan.",
|
||||
"code": "LIMIT_EXCEEDED",
|
||||
"statusCode": 400
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Code | Status | Description |
|
||||
| ------------------- | ------ | ----------------------------- |
|
||||
| `TOO_MANY_REQUESTS` | 429 | HTTP rate limit exceeded |
|
||||
| `LIMIT_EXCEEDED` | 400 | Resource usage limit exceeded |
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Authentication](/docs/developers/getting-started/authentication) - API authentication guide
|
||||
- [API Versioning](/docs/developers/api/versioning) - API version management
|
||||
- [First API Call](/docs/developers/getting-started/first-api-call) - Getting started with the API
|
||||
@@ -0,0 +1,504 @@
|
||||
---
|
||||
title: Recipients API
|
||||
description: Add and manage envelope recipients via API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
<Callout type="warn">
|
||||
This guide may not reflect the latest endpoints or parameters. For an always up-to-date reference,
|
||||
see the [OpenAPI Reference](https://openapi.documenso.com).
|
||||
</Callout>
|
||||
|
||||
## Recipient Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"envelopeId": "clu1abc2def3ghi4jkl",
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"role": "SIGNER",
|
||||
"signingOrder": 1,
|
||||
"token": "abc123...",
|
||||
"signedAt": "2024-01-15T10:30:00Z",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
| --------------- | -------------- | ------------------------------------- |
|
||||
| `id` | number | Unique recipient identifier |
|
||||
| `envelopeId` | string | ID of the associated envelope |
|
||||
| `email` | string | Recipient's email address |
|
||||
| `name` | string | Recipient's display name |
|
||||
| `role` | string | Recipient role (see below) |
|
||||
| `signingOrder` | number \| null | Order in sequential signing |
|
||||
| `token` | string | Unique token for signing URL |
|
||||
| `signedAt` | string \| null | ISO timestamp when signed |
|
||||
| `readStatus` | string | `NOT_OPENED` or `OPENED` |
|
||||
| `signingStatus` | string | `NOT_SIGNED`, `SIGNED`, or `REJECTED` |
|
||||
| `sendStatus` | string | `NOT_SENT` or `SENT` |
|
||||
|
||||
---
|
||||
|
||||
## Recipient Roles
|
||||
|
||||
| Role | Description |
|
||||
| ----------- | -------------------------------------------------------------- |
|
||||
| `SIGNER` | Must sign the document. Required fields must be completed. |
|
||||
| `APPROVER` | Must approve the document before signers can proceed. |
|
||||
| `VIEWER` | Can view the document but takes no action. |
|
||||
| `CC` | Receives a copy of the completed document. No action required. |
|
||||
| `ASSISTANT` | Can fill in fields on behalf of another recipient. |
|
||||
|
||||
---
|
||||
|
||||
## Get Recipient
|
||||
|
||||
Retrieve a single recipient by ID.
|
||||
|
||||
```
|
||||
GET /api/v2/envelope/recipient/{recipientId}
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl "https://app.documenso.com/api/v2/envelope/recipient/789" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/recipient/789',
|
||||
{
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const recipient = await response.json();
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
Returns the full recipient object including fields.
|
||||
|
||||
---
|
||||
|
||||
## Create Recipients
|
||||
|
||||
Add one or more recipients to an envelope.
|
||||
|
||||
```
|
||||
|
||||
POST /api/v2/envelope/recipient/create-many
|
||||
|
||||
````
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | --------------------------------------- |
|
||||
| `envelopeId` | string | Yes | ID of the envelope to add recipients to |
|
||||
| `data` | array | Yes | Array of recipient objects |
|
||||
|
||||
Each item in the `data` array:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| -------------- | -------- | -------- | ------------------------------------------ |
|
||||
| `email` | string | Yes | Recipient's email address |
|
||||
| `name` | string | Yes | Recipient's display name (max 255 chars) |
|
||||
| `role` | string | Yes | Recipient role (see Recipient Roles above) |
|
||||
| `signingOrder` | number | No | Position in sequential signing |
|
||||
| `accessAuth` | string[] | No | Access authentication types |
|
||||
| `actionAuth` | string[] | No | Action authentication types |
|
||||
|
||||
### Example
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/recipient/create-many" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "clu1abc2def3ghi4jkl",
|
||||
"data": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"role": "SIGNER",
|
||||
"signingOrder": 1
|
||||
},
|
||||
{
|
||||
"email": "approver@example.com",
|
||||
"name": "Jane Smith",
|
||||
"role": "APPROVER",
|
||||
"signingOrder": 0
|
||||
}
|
||||
]
|
||||
}'
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/recipient/create-many',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'clu1abc2def3ghi4jkl',
|
||||
data: [
|
||||
{
|
||||
email: 'signer@example.com',
|
||||
name: 'John Doe',
|
||||
role: 'SIGNER',
|
||||
signingOrder: 1,
|
||||
},
|
||||
{
|
||||
email: 'approver@example.com',
|
||||
name: 'Jane Smith',
|
||||
role: 'APPROVER',
|
||||
signingOrder: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const { data: recipients } = await response.json();
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 789,
|
||||
"envelopeId": "clu1abc2def3ghi4jkl",
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"role": "SIGNER",
|
||||
"signingOrder": 1,
|
||||
"token": "abc123def456",
|
||||
"signedAt": null,
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "NOT_SENT"
|
||||
},
|
||||
{
|
||||
"id": 790,
|
||||
"envelopeId": "clu1abc2def3ghi4jkl",
|
||||
"email": "approver@example.com",
|
||||
"name": "Jane Smith",
|
||||
"role": "APPROVER",
|
||||
"signingOrder": 0,
|
||||
"token": "def456ghi789",
|
||||
"signedAt": null,
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "NOT_SENT"
|
||||
}
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Update Recipients
|
||||
|
||||
Update one or more recipients on an envelope. Only available for envelopes that are not yet completed.
|
||||
|
||||
```
|
||||
POST /api/v2/envelope/recipient/update-many
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | ------ | -------- | -------------------------------------------- |
|
||||
| `envelopeId` | string | Yes | ID of the envelope containing the recipients |
|
||||
| `data` | array | Yes | Array of recipient update objects |
|
||||
|
||||
Each item in the `data` array:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| -------------- | -------- | -------- | ---------------------------------- |
|
||||
| `id` | number | Yes | ID of the recipient to update |
|
||||
| `email` | string | No | New email address |
|
||||
| `name` | string | No | New display name (max 255 chars) |
|
||||
| `role` | string | No | New recipient role |
|
||||
| `signingOrder` | number | No | New position in sequential signing |
|
||||
| `accessAuth` | string[] | No | Access authentication types |
|
||||
| `actionAuth` | string[] | No | Action authentication types |
|
||||
|
||||
### Example
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/recipient/update-many" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"envelopeId": "clu1abc2def3ghi4jkl",
|
||||
"data": [
|
||||
{
|
||||
"id": 789,
|
||||
"name": "Jane Doe",
|
||||
"signingOrder": 2
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/recipient/update-many',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'clu1abc2def3ghi4jkl',
|
||||
data: [
|
||||
{
|
||||
id: 789,
|
||||
name: 'Jane Doe',
|
||||
signingOrder: 2,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const { data: updatedRecipients } = await response.json();
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
Returns the updated recipient objects in a `data` array.
|
||||
|
||||
---
|
||||
|
||||
## Delete Recipient
|
||||
|
||||
Remove a recipient from an envelope. Only available for envelopes that are not yet completed.
|
||||
|
||||
```
|
||||
|
||||
POST /api/v2/envelope/recipient/delete
|
||||
|
||||
````
|
||||
|
||||
### Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------- | ------ | -------- | ------------------------------- |
|
||||
| `recipientId` | number | Yes | ID of the recipient to remove |
|
||||
|
||||
### Example
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/recipient/delete" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"recipientId": 789
|
||||
}'
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(
|
||||
'https://app.documenso.com/api/v2/envelope/recipient/delete',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
recipientId: 789,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
// { "success": true }
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Signing Order
|
||||
|
||||
When an envelope uses sequential signing, recipients sign in a specific order defined by `signingOrder`.
|
||||
|
||||
### Setting Signing Order
|
||||
|
||||
When creating recipients, assign `signingOrder` values to control the sequence:
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/recipient/create-many', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'clu1abc2def3ghi4jkl',
|
||||
data: [
|
||||
{
|
||||
email: 'approver@example.com',
|
||||
name: 'Approver',
|
||||
role: 'APPROVER',
|
||||
signingOrder: 0, // Approvers typically go first
|
||||
},
|
||||
{
|
||||
email: 'first@example.com',
|
||||
name: 'First Signer',
|
||||
role: 'SIGNER',
|
||||
signingOrder: 1,
|
||||
},
|
||||
{
|
||||
email: 'second@example.com',
|
||||
name: 'Second Signer',
|
||||
role: 'SIGNER',
|
||||
signingOrder: 2,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
To enable sequential signing, set `signingOrder` to `SEQUENTIAL` in the envelope metadata when
|
||||
creating or updating the envelope. See the [Documents API](/docs/developers/api/documents) for
|
||||
details.
|
||||
</Callout>
|
||||
|
||||
### Signing Order Behavior
|
||||
|
||||
- Recipients with lower `signingOrder` values sign first
|
||||
- Recipients with the same `signingOrder` can sign simultaneously
|
||||
- `CC` recipients receive the document after all signing is complete
|
||||
- `APPROVER` recipients must approve before signers with higher order values
|
||||
|
||||
---
|
||||
|
||||
## Authentication Options
|
||||
|
||||
For enhanced security, you can require additional authentication when recipients access or sign a document.
|
||||
|
||||
### Access Authentication
|
||||
|
||||
Controls who can view the document:
|
||||
|
||||
| Type | Description |
|
||||
| ----------------- | ------------------------------ |
|
||||
| `ACCOUNT` | Recipient must be logged in |
|
||||
| `TWO_FACTOR_AUTH` | Recipient must verify with 2FA |
|
||||
|
||||
### Action Authentication
|
||||
|
||||
Controls who can sign the document:
|
||||
|
||||
| Type | Description |
|
||||
| ----------------- | ---------------------------------------- |
|
||||
| `ACCOUNT` | Recipient must be logged in |
|
||||
| `PASSKEY` | Require passkey authentication |
|
||||
| `TWO_FACTOR_AUTH` | Require 2FA code |
|
||||
| `PASSWORD` | Require password verification |
|
||||
| `EXPLICIT_NONE` | Explicitly disable action authentication |
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/recipient/create-many', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
envelopeId: 'clu1abc2def3ghi4jkl',
|
||||
data: [
|
||||
{
|
||||
email: 'signer@example.com',
|
||||
name: 'John Doe',
|
||||
role: 'SIGNER',
|
||||
accessAuth: ['ACCOUNT'],
|
||||
actionAuth: ['PASSKEY', 'TWO_FACTOR_AUTH'],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Status | Description |
|
||||
| ------ | ------------------------------------------------ |
|
||||
| `400` | Invalid request body or recipient already exists |
|
||||
| `400` | Envelope is already completed |
|
||||
| `401` | Invalid or missing API key |
|
||||
| `404` | Envelope or recipient not found |
|
||||
| `500` | Server error |
|
||||
|
||||
### Example Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Recipient already exists"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Documents API](/docs/developers/api/documents) - Create and manage envelopes
|
||||
- [Fields API](/docs/developers/api/fields) - Add signature fields for recipients
|
||||
@@ -0,0 +1,373 @@
|
||||
---
|
||||
title: Teams API
|
||||
description: Manage team resources, documents, and templates with team-scoped API tokens.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
<Callout type="warn">
|
||||
This guide may not reflect the latest endpoints or parameters. For an always up-to-date reference,
|
||||
see the [OpenAPI Reference](https://openapi.documenso.com).
|
||||
</Callout>
|
||||
|
||||
## Team Object
|
||||
|
||||
A team object contains the following properties:
|
||||
|
||||
| Property | Type | Description |
|
||||
| ----------------- | -------------- | --------------------------------------------------- |
|
||||
| `id` | number | Unique team identifier |
|
||||
| `name` | string | Team display name |
|
||||
| `url` | string | Unique team URL slug |
|
||||
| `createdAt` | string | ISO 8601 timestamp |
|
||||
| `avatarImageId` | string \| null | ID of the team's avatar image |
|
||||
| `organisationId` | string | ID of the parent organisation |
|
||||
| `currentTeamRole` | string | Your role in the team: `ADMIN`, `MANAGER`, `MEMBER` |
|
||||
|
||||
### Example Team Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "Engineering",
|
||||
"url": "engineering",
|
||||
"createdAt": "2025-01-15T10:30:00.000Z",
|
||||
"avatarImageId": null,
|
||||
"organisationId": "org_abc123",
|
||||
"currentTeamRole": "ADMIN"
|
||||
}
|
||||
```
|
||||
|
||||
## Team-Scoped API Tokens
|
||||
|
||||
API tokens in Documenso are always scoped to a specific team. When you create an API token, it is associated with the team you're currently working in.
|
||||
|
||||
### How Team Scoping Works
|
||||
|
||||
- Each API token belongs to exactly one team
|
||||
- All API operations using that token automatically access that team's resources
|
||||
- Documents, templates, and other resources created via the API belong to the token's team
|
||||
- You cannot access resources from other teams with a single token
|
||||
|
||||
### Creating Team-Scoped Tokens
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Navigate to your team's settings
|
||||
</Step>
|
||||
<Step>
|
||||
Go to **API Tokens**
|
||||
</Step>
|
||||
<Step>
|
||||
Click **Create Token**
|
||||
</Step>
|
||||
<Step>
|
||||
The token will be scoped to the current team
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="info">
|
||||
To work with multiple teams via API, create separate tokens for each team.
|
||||
</Callout>
|
||||
|
||||
### Token Permissions
|
||||
|
||||
Your API token inherits permissions based on your role in the team:
|
||||
|
||||
| Role | Permissions |
|
||||
| --------- | ------------------------------------------------ |
|
||||
| `ADMIN` | Full access to all team resources and settings |
|
||||
| `MANAGER` | Create, edit, and delete documents and templates |
|
||||
| `MEMBER` | Create and manage own documents |
|
||||
|
||||
## Working with Team Documents
|
||||
|
||||
When you use a team-scoped API token, all document operations are automatically scoped to that team.
|
||||
|
||||
### Create a Team Document
|
||||
|
||||
Documents created with a team token belong to that team:
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
|
||||
-H "Authorization: api_team_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F 'payload={
|
||||
"type": "DOCUMENT",
|
||||
"title": "Team Contract",
|
||||
"recipients": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"name": "John Smith",
|
||||
"role": "SIGNER"
|
||||
}
|
||||
]
|
||||
}' \
|
||||
-F "files=@./contract.pdf;type=application/pdf"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const TEAM_API_TOKEN = process.env.DOCUMENSO_TEAM_API_TOKEN;
|
||||
|
||||
const form = new FormData();
|
||||
|
||||
const payload = {
|
||||
type: 'DOCUMENT',
|
||||
title: 'Team Contract',
|
||||
recipients: [
|
||||
{
|
||||
email: 'signer@example.com',
|
||||
name: 'John Smith',
|
||||
role: 'SIGNER',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
form.append('payload', JSON.stringify(payload));
|
||||
form.append('files', fs.createReadStream('./contract.pdf'), {
|
||||
contentType: 'application/pdf',
|
||||
});
|
||||
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: TEAM_API_TOKEN,
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
const { id } = await response.json();
|
||||
console.log('Created team document:', id);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### List Team Documents
|
||||
|
||||
Retrieve all documents belonging to the team:
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
# List all team documents
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope" \
|
||||
-H "Authorization: api_team_xxxxxxxxxxxxxxxx"
|
||||
|
||||
# Filter by status
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope?status=PENDING" \
|
||||
-H "Authorization: api_team_xxxxxxxxxxxxxxxx"
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: TEAM_API_TOKEN,
|
||||
},
|
||||
});
|
||||
|
||||
const { data, pagination } = await response.json();
|
||||
console.log(`Found ${pagination.totalItems} team documents`);
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Working with Team Templates
|
||||
|
||||
Templates created with a team token are shared across the team.
|
||||
|
||||
### Create a Team Template
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/template/create" \
|
||||
-H "Authorization: api_team_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F 'payload={
|
||||
"title": "NDA Template",
|
||||
"recipients": [
|
||||
{
|
||||
"email": "placeholder@example.com",
|
||||
"name": "Signer",
|
||||
"role": "SIGNER",
|
||||
"fields": [
|
||||
{
|
||||
"identifier": 0,
|
||||
"type": "SIGNATURE",
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}' \
|
||||
-F "files=@./nda-template.pdf;type=application/pdf"
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const form = new FormData();
|
||||
|
||||
const payload = {
|
||||
title: 'NDA Template',
|
||||
recipients: [
|
||||
{
|
||||
email: 'placeholder@example.com',
|
||||
name: 'Signer',
|
||||
role: 'SIGNER',
|
||||
fields: [
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'SIGNATURE',
|
||||
page: 1,
|
||||
positionX: 10,
|
||||
positionY: 80,
|
||||
width: 30,
|
||||
height: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
form.append('payload', JSON.stringify(payload));
|
||||
form.append('files', fs.createReadStream('./nda-template.pdf'), {
|
||||
contentType: 'application/pdf',
|
||||
});
|
||||
|
||||
const response = await fetch('https://app.documenso.com/api/v2/template/create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: TEAM_API_TOKEN,
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
const template = await response.json();
|
||||
console.log('Created team template:', template.id);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### List Team Templates
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X GET "https://app.documenso.com/api/v2/template" \
|
||||
-H "Authorization: api_team_xxxxxxxxxxxxxxxx"
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/template', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: TEAM_API_TOKEN,
|
||||
},
|
||||
});
|
||||
|
||||
const { data } = await response.json();
|
||||
console.log('Team templates:', data);
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Team Member Roles
|
||||
|
||||
| Role | Description |
|
||||
| --------- | ------------------------------------------------------------------- |
|
||||
| `ADMIN` | Full control over team settings, members, and all resources |
|
||||
| `MANAGER` | Can manage documents, templates, and view team resources |
|
||||
| `MEMBER` | Can create and manage their own documents within the team |
|
||||
|
||||
### Document Visibility
|
||||
|
||||
Team documents have visibility settings that control who can access them:
|
||||
|
||||
| Visibility | Description |
|
||||
| ------------------ | ------------------------------------------------ |
|
||||
| `EVERYONE` | All team members can view the document |
|
||||
| `MANAGER_AND_ABOVE`| Only managers and admins can view |
|
||||
| `ADMIN` | Only admins can view |
|
||||
|
||||
Set visibility when creating a document:
|
||||
|
||||
```typescript
|
||||
const payload = {
|
||||
type: 'DOCUMENT',
|
||||
title: 'Confidential Agreement',
|
||||
visibility: 'ADMIN', // Only team admins can view
|
||||
recipients: [...],
|
||||
};
|
||||
````
|
||||
|
||||
## Multi-Team Workflow
|
||||
|
||||
To work with multiple teams, create and manage separate API tokens for each team.
|
||||
|
||||
### Example: Sync Documents Across Teams
|
||||
|
||||
```typescript
|
||||
// Tokens for different teams
|
||||
const SALES_TEAM_TOKEN = process.env.SALES_TEAM_API_TOKEN;
|
||||
const LEGAL_TEAM_TOKEN = process.env.LEGAL_TEAM_API_TOKEN;
|
||||
|
||||
// Get pending documents from sales team
|
||||
const salesResponse = await fetch('https://app.documenso.com/api/v2/envelope?status=PENDING', {
|
||||
headers: { Authorization: SALES_TEAM_TOKEN },
|
||||
});
|
||||
const salesDocs = await salesResponse.json();
|
||||
|
||||
// Get completed documents from legal team
|
||||
const legalResponse = await fetch('https://app.documenso.com/api/v2/envelope?status=COMPLETED', {
|
||||
headers: { Authorization: LEGAL_TEAM_TOKEN },
|
||||
});
|
||||
const legalDocs = await legalResponse.json();
|
||||
|
||||
console.log(`Sales team: ${salesDocs.pagination.totalItems} pending`);
|
||||
console.log(`Legal team: ${legalDocs.pagination.totalItems} completed`);
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
| Status | Description |
|
||||
| ------ | ------------------------------------------------- |
|
||||
| `401` | Invalid or expired API token |
|
||||
| `403` | Token doesn't have permission for this operation |
|
||||
| `404` | Resource not found or not accessible by this team |
|
||||
|
||||
### Example Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "You do not have permission to access this resource"
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
API tokens can only access resources belonging to their associated team. Attempting to access
|
||||
resources from another team returns a 403 or 404 error.
|
||||
</Callout>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Documents API](/docs/developers/api/documents) - Create and manage documents
|
||||
- [Templates API](/docs/developers/api/templates) - Work with document templates
|
||||
- [Authentication](/docs/developers/getting-started/authentication) - Create and manage API tokens
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: API Versioning
|
||||
description: Versioning information for the Documenso public API.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## Overview
|
||||
|
||||
Documenso uses API versioning to manage changes to the public API. This allows us to introduce new features, fix bugs, and make other changes without breaking existing integrations.
|
||||
|
||||
<Callout type="info">The current version of the API is `v2`.</Callout>
|
||||
|
||||
The API version is specified in the URL. For example, the base URL for the `v2` API is `https://app.documenso.com/api/v2`.
|
||||
|
||||
We may make changes to the API without incrementing the version number. We will always try to avoid breaking changes, but in some cases, it may be necessary to make changes that are not backward compatible. In these cases, we will increment the version number and provide information about the changes in the release notes.
|
||||
|
||||
Also, we may deprecate certain features or endpoints in the API. When we deprecate a feature or endpoint, we will provide information about the deprecation in the release notes and give a timeline for when the feature or endpoint will be removed.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Authentication](/docs/developers/getting-started/authentication) - API authentication guide
|
||||
- [Rate Limits](/docs/developers/api/rate-limits) - API rate limit details
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Contributing Translations
|
||||
description: Learn how to contribute translations to Documenso and become part of our community.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
We are always open for help with translations! Currently we utilise AI to generate the initial translations for new languages, which are then improved over time by our awesome community.
|
||||
|
||||
If you are looking for development notes on translations, you can find them [here](/docs/developers/local-development/translations).
|
||||
|
||||
<Callout type="info">
|
||||
Contributions are made through GitHub Pull Requests, so you will need a GitHub account to
|
||||
contribute.
|
||||
</Callout>
|
||||
|
||||
## Overview
|
||||
|
||||
We store our translations in PO files, which are located in our GitHub repository [here](https://github.com/documenso/documenso/tree/main/packages/lib/translations).
|
||||
|
||||
The translation files are organized into folders represented by their respective language codes (`en` for English, `de` for German, etc).
|
||||
|
||||
Each PO file contains translations which look like this:
|
||||
|
||||
```po
|
||||
#: apps/remix/app/(signing)/sign/[token]/no-longer-available.tsx:61
|
||||
msgid "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
|
||||
msgstr "Möchten Sie auffällige Signatur-Links wie diesen senden? <0>Überprüfen Sie Documenso.</0>"
|
||||
```
|
||||
|
||||
- `msgid`: The original text in English (never edit this manually)
|
||||
- `msgstr`: The translated text in the target language
|
||||
|
||||
<Callout type="warn">
|
||||
Notice the `<0>` tags? These represent HTML elements and must remain in both the `msgid` and `msgstr`. Make sure to translate the content between these tags while keeping the tags intact.
|
||||
</Callout>
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Updating Existing Translations
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Fork the repository
|
||||
</Step>
|
||||
<Step>
|
||||
Navigate to the appropriate language folder and open the PO file you want to update
|
||||
</Step>
|
||||
<Step>
|
||||
Make your changes, ensuring you follow the PO file format
|
||||
</Step>
|
||||
<Step>
|
||||
Commit your changes with a message such as <code>chore: update German translations</code>
|
||||
</Step>
|
||||
<Step>
|
||||
Create a Pull Request
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Adding a New Language
|
||||
|
||||
If you want to add translations for a language that doesn't exist yet:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Create an issue in our GitHub repository requesting the addition of the new language
|
||||
</Step>
|
||||
<Step>
|
||||
Wait for our team to review and approve the request
|
||||
</Step>
|
||||
<Step>
|
||||
Once approved, we will set up the necessary files and kickstart the translations with AI to
|
||||
provide initial coverage
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Need Help?
|
||||
|
||||
<Callout type="info">
|
||||
If you have any questions, hop into our [Discord](https://documen.so/discord) and ask us directly!
|
||||
</Callout>
|
||||
|
||||
Thank you for helping make Documenso more accessible to users around the world!
|
||||
|
||||
## See Also
|
||||
|
||||
- [Translations (Development)](/docs/developers/local-development/translations) - Technical guide to translations in code
|
||||
- [Contributing Guide](/docs/developers/contributing) - General contributing guidelines
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
title: Contributing to Documenso
|
||||
description: Learn how to contribute to Documenso and become part of our community.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Overview
|
||||
|
||||
If you plan to contribute to Documenso, please take a moment to feel awesome. People like you are what open source is about. Any contributions, no matter how big or small, are highly appreciated.
|
||||
|
||||
This guide will help you get started with contributing to Documenso.
|
||||
|
||||
## Before Getting Started
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Check the existing issues and pull requests
|
||||
|
||||
Search the existing [issues](https://github.com/documenso/documenso/issues) to see if someone else reported the same issue. Or, check the [existing PRs](https://github.com/documenso/documenso/pulls) to see if someone else is already working on the same thing.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Creating a new issue
|
||||
|
||||
If there is no issue or PR for the problem you are facing, feel free to create a new issue. Make sure to provide as much detail as possible, including the steps to reproduce the issue.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Picking an existing issue
|
||||
|
||||
If you pick an existing issue, take into consideration the discussion on the issue.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Contributor license agreement
|
||||
|
||||
Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Taking Issues
|
||||
|
||||
Before taking an issue, ensure that:
|
||||
|
||||
- The issue has been assigned the public label.
|
||||
- The issue is clearly defined and understood.
|
||||
- No one has been assigned to the issue.
|
||||
- No one has expressed the intention to work on it.
|
||||
|
||||
After that:
|
||||
|
||||
1. Comment on the issue with your intention to work on it.
|
||||
2. Start working on the issue.
|
||||
|
||||
Feel free to ask for help, clarification or guidance if needed. We are here to help you.
|
||||
|
||||
## Developing
|
||||
|
||||
The development branch is `main`, and all pull requests should be made against this branch. Here's how you can get started with developing:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Set up Documenso locally
|
||||
|
||||
To set up your local environment, check out the [local development](/docs/developers/local-development) guide.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Pick a task
|
||||
|
||||
Find an issue to work on or create a new one.
|
||||
|
||||
> Before working on an issue, ensure that no one else is working on it. If no one is assigned to the issue, you can pick it up by leaving a comment and asking to assign it to you.
|
||||
|
||||
Before creating a new issue, check the existing issues to see if someone else has already reported it.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Create a new branch
|
||||
|
||||
After you're assigned an issue, you can start working on it. Create a new branch for your feature or bug fix.
|
||||
|
||||
When creating a branch, make sure that the branch name:
|
||||
|
||||
- starts with the correct prefix: `feat/` for new features, `fix/` for bug fixes, etc.
|
||||
- includes the issue ID you are working on (if applicable).
|
||||
- is descriptive.
|
||||
|
||||
```sh
|
||||
git checkout -b feat/issue-id-your-branch-name
|
||||
|
||||
## Example
|
||||
git checkout -b feat/1234-add-share-button-to-articles
|
||||
```
|
||||
|
||||
In the pull request description, include `references #yyyy` or `fixes #yyyy` to link it to the issue you are working on.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Implement your changes
|
||||
|
||||
Start working on the issue you picked up and implement the changes. Make sure to test your changes locally and ensure that they work as expected.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Open a pull request
|
||||
|
||||
After implementing your changes, open a pull request against the `main` branch.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Callout type="info">
|
||||
If you need help getting started, [join us on Discord](https://documen.so/discord).
|
||||
</Callout>
|
||||
|
||||
## Building
|
||||
|
||||
Before pushing code or creating pull requests, please ensure you can successfully create a successful production build. You can build the project by running the following command in your terminal:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Once the project builds successfully, you can push your code changes or create a pull request.
|
||||
|
||||
<Callout type="info">
|
||||
Remember to run tests and perform any necessary checks before finalizing your changes. As a
|
||||
result, we can collaborate more effectively and maintain a high standard of code quality in our
|
||||
project.
|
||||
</Callout>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Local Development](/docs/developers/local-development) - Set up your development environment
|
||||
- [Contributing Translations](/docs/developers/contributing/contributing-translations) - Help translate Documenso
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Contributing",
|
||||
"pages": ["contributing-translations"]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Demo Environment
|
||||
description: Use the demo environment to try out the Documenso platform and its features.
|
||||
---
|
||||
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Overview
|
||||
|
||||
The demo (staging) environment is a sandbox environment that replicates the production environment. It has the same features and capabilities as the production environment, but is intended for development and testing purposes.
|
||||
|
||||
You can use it to try out the Documenso platform and its features before committing to a paid plan.
|
||||
|
||||
## How to Use the Demo Environment
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Navigate to the staging environment
|
||||
|
||||
Go to the [staging environment](https://stg-app.documenso.com).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create an account
|
||||
|
||||
You need to create a new account for the demo environment. You can't use your production account.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Pick a paid plan
|
||||
|
||||
Choose the appropriate plan for your needs.
|
||||
|
||||
You can also use the free plan but it's limited to 5 documents per month and up to 10 recipients per document.
|
||||
|
||||
Whatever plan you choose, you can upgrade later.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Use a test card
|
||||
|
||||
To upgrade to a paid plan, you can use a test card. Example:
|
||||
|
||||
```
|
||||
Card number: 4242 4242 4242 4242
|
||||
Expiry date: 02/2030 (or any valid future date)
|
||||
CVV: 123
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Use the platform
|
||||
|
||||
You can then try out the platform and its features.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Issues, questions and feedback
|
||||
|
||||
If you have any issues, questions or feedback, please reach out to us on the [Documenso Discord](https://documen.so/discord) or [GitHub](https://github.com/documenso/documenso/issues).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -0,0 +1,306 @@
|
||||
---
|
||||
title: Authoring
|
||||
description: Embed document and template creation directly in your application.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
In addition to embedding signing, Documenso supports embedded authoring. It allows your users to create and edit documents and templates without leaving your application.
|
||||
|
||||
<Callout type="warn">
|
||||
Embedded authoring is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is
|
||||
also available as a paid add-on for the [Platform Plan](https://documen.so/platform-cta-pricing).
|
||||
Contact sales for access.
|
||||
</Callout>
|
||||
|
||||
## Components
|
||||
|
||||
The SDK provides four authoring components:
|
||||
|
||||
| Component | Purpose |
|
||||
| ----------------------- | ----------------------- |
|
||||
| `EmbedCreateDocumentV1` | Create new documents |
|
||||
| `EmbedCreateTemplateV1` | Create new templates |
|
||||
| `EmbedUpdateDocumentV1` | Edit existing documents |
|
||||
| `EmbedUpdateTemplateV1` | Edit existing templates |
|
||||
|
||||
All authoring components require a **presign token** for authentication instead of a regular token.
|
||||
|
||||
---
|
||||
|
||||
## Presign Tokens
|
||||
|
||||
Before using any authoring component, obtain a presign token from your backend:
|
||||
|
||||
```
|
||||
POST /api/v2/embedding/create-presign-token
|
||||
```
|
||||
|
||||
This endpoint requires your Documenso API key. The token has a default expiration of 1 hour.
|
||||
|
||||
See the [API documentation](https://openapi.documenso.com/reference#tag/embedding) for full details.
|
||||
|
||||
<Callout type="warn">
|
||||
Presign tokens should be created server-side. Never expose your API key in client-side code.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Creating Documents
|
||||
|
||||
```jsx
|
||||
import { EmbedCreateDocumentV1 } from '@documenso/embed-react';
|
||||
|
||||
const DocumentCreator = ({ presignToken }) => {
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<EmbedCreateDocumentV1
|
||||
presignToken={presignToken}
|
||||
externalId="order-12345"
|
||||
onDocumentCreated={(data) => {
|
||||
console.log('Document created:', data.documentId);
|
||||
console.log('External ID:', data.externalId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating Templates
|
||||
|
||||
```jsx
|
||||
import { EmbedCreateTemplateV1 } from '@documenso/embed-react';
|
||||
|
||||
const TemplateCreator = ({ presignToken }) => {
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<EmbedCreateTemplateV1
|
||||
presignToken={presignToken}
|
||||
externalId="template-12345"
|
||||
onTemplateCreated={(data) => {
|
||||
console.log('Template created:', data.templateId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Editing Documents
|
||||
|
||||
```jsx
|
||||
import { EmbedUpdateDocumentV1 } from '@documenso/embed-react';
|
||||
|
||||
const DocumentEditor = ({ presignToken, documentId }) => {
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<EmbedUpdateDocumentV1
|
||||
presignToken={presignToken}
|
||||
documentId={documentId}
|
||||
externalId="order-12345"
|
||||
onDocumentUpdated={(data) => {
|
||||
console.log('Document updated:', data.documentId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Editing Templates
|
||||
|
||||
```jsx
|
||||
import { EmbedUpdateTemplateV1 } from '@documenso/embed-react';
|
||||
|
||||
const TemplateEditor = ({ presignToken, templateId }) => {
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<EmbedUpdateTemplateV1
|
||||
presignToken={presignToken}
|
||||
templateId={templateId}
|
||||
externalId="template-12345"
|
||||
onTemplateUpdated={(data) => {
|
||||
console.log('Template updated:', data.templateId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Props
|
||||
|
||||
### All Authoring Components
|
||||
|
||||
| Prop | Type | Required | Description |
|
||||
| ------------------ | --------- | -------- | -------------------------------------------------------- |
|
||||
| `presignToken` | `string` | Yes | Authentication token from your backend |
|
||||
| `externalId` | `string` | No | Your reference ID to link with the document or template |
|
||||
| `host` | `string` | No | Custom host URL. Defaults to `https://app.documenso.com` |
|
||||
| `css` | `string` | No | Custom CSS string (Platform Plan) |
|
||||
| `cssVars` | `object` | No | CSS variable overrides (Platform Plan) |
|
||||
| `darkModeDisabled` | `boolean` | No | Disable dark mode (Platform Plan) |
|
||||
| `className` | `string` | No | CSS class for the iframe |
|
||||
| `features` | `object` | No | Feature toggles for the authoring experience |
|
||||
|
||||
### Update Components Only
|
||||
|
||||
| Prop | Type | Required | Description |
|
||||
| ---------------- | --------- | -------- | ---------------------------------------------------------- |
|
||||
| `documentId` | `number` | Yes | The document ID to edit (for document update component) |
|
||||
| `templateId` | `number` | Yes | The template ID to edit (for template update component) |
|
||||
| `onlyEditFields` | `boolean` | No | Restrict editing to fields only, skipping recipient config |
|
||||
|
||||
---
|
||||
|
||||
## Feature Toggles
|
||||
|
||||
Customize what options are available in the authoring experience:
|
||||
|
||||
```jsx
|
||||
<EmbedCreateDocumentV1
|
||||
presignToken={presignToken}
|
||||
features={{
|
||||
allowConfigureSignatureTypes: true,
|
||||
allowConfigureLanguage: true,
|
||||
allowConfigureDateFormat: true,
|
||||
allowConfigureTimezone: true,
|
||||
allowConfigureRedirectUrl: true,
|
||||
allowConfigureCommunication: true,
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event Callbacks
|
||||
|
||||
All creation callbacks receive:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ---------------------------- | -------- | --------------------------------------- |
|
||||
| `documentId` or `templateId` | `number` | The ID of the created or updated item |
|
||||
| `externalId` | `string` | Your external reference ID, if provided |
|
||||
|
||||
---
|
||||
|
||||
## Field-Only Editing
|
||||
|
||||
Restrict users to editing fields only, skipping recipient configuration:
|
||||
|
||||
```jsx
|
||||
<EmbedUpdateDocumentV1
|
||||
presignToken={presignToken}
|
||||
documentId={123}
|
||||
onlyEditFields={true}
|
||||
onDocumentUpdated={(data) => {
|
||||
console.log('Fields updated:', data.documentId);
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Integration Example
|
||||
|
||||
This example shows a full workflow where users create a document and then edit it:
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EmbedCreateDocumentV1, EmbedUpdateDocumentV1 } from '@documenso/embed-react';
|
||||
|
||||
const DocumentManager = ({ presignToken }) => {
|
||||
const [documentId, setDocumentId] = useState(null);
|
||||
const [mode, setMode] = useState('create');
|
||||
|
||||
if (mode === 'success') {
|
||||
return (
|
||||
<div>
|
||||
<h2>Document updated successfully</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
setDocumentId(null);
|
||||
setMode('create');
|
||||
}}
|
||||
>
|
||||
Create Another Document
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (mode === 'edit' && documentId) {
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<button onClick={() => setMode('create')}>Back to Create</button>
|
||||
<EmbedUpdateDocumentV1
|
||||
presignToken={presignToken}
|
||||
documentId={documentId}
|
||||
onDocumentUpdated={(data) => {
|
||||
console.log('Document updated:', data.documentId);
|
||||
setMode('success');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ height: '800px', width: '100%' }}>
|
||||
<EmbedCreateDocumentV1
|
||||
presignToken={presignToken}
|
||||
features={{
|
||||
allowConfigureSignatureTypes: true,
|
||||
allowConfigureLanguage: true,
|
||||
}}
|
||||
onDocumentCreated={(data) => {
|
||||
console.log('Document created:', data.documentId);
|
||||
setDocumentId(data.documentId);
|
||||
setMode('edit');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Props
|
||||
|
||||
Pass extra props to the iframe for testing experimental features:
|
||||
|
||||
```jsx
|
||||
<EmbedCreateDocumentV1
|
||||
presignToken="YOUR_PRESIGN_TOKEN"
|
||||
additionalProps={{
|
||||
experimentalFeature: true,
|
||||
customSetting: 'value',
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
Presign tokens expire after 1 hour by default. You can customize this duration based on your
|
||||
security requirements. Generate fresh tokens for each session and avoid caching them on the client
|
||||
side.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Signing embed concepts and props
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Documents API](/docs/developers/api/documents) - Create documents via API
|
||||
- [Templates API](/docs/developers/api/templates) - Create templates via API
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
title: CSS Variables
|
||||
description: Customize the appearance of embedded signing experiences with CSS variables and class targets.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
<Callout type="info">
|
||||
Custom CSS and CSS variables are available on the [Platform
|
||||
Plan](https://documen.so/platform-cta-pricing).
|
||||
</Callout>
|
||||
|
||||
## CSS Variables
|
||||
|
||||
Use the `cssVars` prop on any embed component to override default colors, spacing, and more.
|
||||
|
||||
```jsx
|
||||
<EmbedDirectTemplate
|
||||
token="your-token"
|
||||
cssVars={{
|
||||
background: '#ffffff',
|
||||
foreground: '#000000',
|
||||
primary: '#0000ff',
|
||||
primaryForeground: '#ffffff',
|
||||
radius: '0.5rem',
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### Colors
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------- | --------------------------------- |
|
||||
| `background` | Base background color |
|
||||
| `foreground` | Base text color |
|
||||
| `muted` | Muted/subtle background color |
|
||||
| `mutedForeground` | Muted/subtle text color |
|
||||
| `popover` | Popover/dropdown background color |
|
||||
| `popoverForeground` | Popover/dropdown text color |
|
||||
| `card` | Card background color |
|
||||
| `cardBorder` | Card border color |
|
||||
| `cardBorderTint` | Card border tint/highlight color |
|
||||
| `cardForeground` | Card text color |
|
||||
| `fieldCard` | Field card background color |
|
||||
| `fieldCardBorder` | Field card border color |
|
||||
| `fieldCardForeground` | Field card text color |
|
||||
| `widget` | Widget background color |
|
||||
| `widgetForeground` | Widget text color |
|
||||
| `border` | Default border color |
|
||||
| `input` | Input field border color |
|
||||
| `primary` | Primary action/button color |
|
||||
| `primaryForeground` | Primary action/button text color |
|
||||
| `secondary` | Secondary action/button color |
|
||||
| `secondaryForeground` | Secondary button text color |
|
||||
| `accent` | Accent/highlight color |
|
||||
| `accentForeground` | Accent/highlight text color |
|
||||
| `destructive` | Destructive/danger action color |
|
||||
| `destructiveForeground` | Destructive/danger text color |
|
||||
| `ring` | Focus ring color |
|
||||
| `warning` | Warning/alert color |
|
||||
|
||||
### Spacing
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ---------------------------------- |
|
||||
| `radius` | Border radius size (e.g. `0.5rem`) |
|
||||
|
||||
### Framework Usage
|
||||
|
||||
Pass `cssVars` to any embed component. The syntax varies by framework:
|
||||
|
||||
```jsx
|
||||
// React / Preact
|
||||
<EmbedDirectTemplate token={token} cssVars={cssVars} />
|
||||
|
||||
// Vue
|
||||
<EmbedDirectTemplate :token="token" :cssVars="cssVars" />
|
||||
|
||||
// Svelte
|
||||
<EmbedDirectTemplate {token} cssVars={cssVars} />
|
||||
|
||||
// Solid
|
||||
<EmbedDirectTemplate token={token} cssVars={cssVars} />
|
||||
```
|
||||
|
||||
### Color Formats
|
||||
|
||||
Colors can be specified in any valid CSS format:
|
||||
|
||||
- Hex: `#ff0000`
|
||||
- RGB: `rgb(255, 0, 0)`
|
||||
- HSL: `hsl(0, 100%, 50%)`
|
||||
- Named: `red`
|
||||
|
||||
---
|
||||
|
||||
## Custom CSS
|
||||
|
||||
Use the `css` prop to inject a CSS string for more targeted control:
|
||||
|
||||
```jsx
|
||||
<EmbedDirectTemplate
|
||||
token="your-token"
|
||||
css={`
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS Class Targets
|
||||
|
||||
Specific parts of the embed can be targeted with CSS classes for granular styling.
|
||||
|
||||
### Component Classes
|
||||
|
||||
| Class | Description |
|
||||
| --------------------------------- | --------------------------------------------- |
|
||||
| `.embed--Root` | Main container for the embedded experience |
|
||||
| `.embed--DocumentContainer` | Container for the document and signing widget |
|
||||
| `.embed--DocumentViewer` | Container for the document viewer |
|
||||
| `.embed--DocumentWidget` | The signing widget container |
|
||||
| `.embed--DocumentWidgetContainer` | Outer container for the signing widget |
|
||||
| `.embed--DocumentWidgetHeader` | Header section of the signing widget |
|
||||
| `.embed--DocumentWidgetContent` | Main content area of the signing widget |
|
||||
| `.embed--DocumentWidgetForm` | Form section within the signing widget |
|
||||
| `.embed--DocumentWidgetFooter` | Footer section of the signing widget |
|
||||
| `.embed--WaitingForTurn` | Waiting screen when it is not the user's turn |
|
||||
| `.embed--DocumentCompleted` | Completion screen after signing |
|
||||
| `.field--FieldRootContainer` | Base container for document fields |
|
||||
|
||||
### Field Data Attributes
|
||||
|
||||
Fields expose data attributes for state-based styling:
|
||||
|
||||
| Attribute | Values | Description |
|
||||
| ------------------- | ---------------------------------------------- | ------------------------------------ |
|
||||
| `[data-field-type]` | `SIGNATURE`, `TEXT`, `CHECKBOX`, `RADIO`, etc. | The type of field |
|
||||
| `[data-inserted]` | `true`, `false` | Whether the field has been filled |
|
||||
| `[data-validate]` | `true`, `false` | Whether the field is being validated |
|
||||
|
||||
### Example
|
||||
|
||||
```css
|
||||
/* Style signature fields */
|
||||
.field--FieldRootContainer[data-field-type='SIGNATURE'] {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
/* Style filled fields */
|
||||
.field--FieldRootContainer[data-inserted='true'] {
|
||||
background-color: var(--primary);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/* Custom widget styling */
|
||||
.embed--DocumentWidget {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
```
|
||||
|
||||
### Additional Examples
|
||||
|
||||
```css
|
||||
/* Style all field containers with transitions */
|
||||
.field--FieldRootContainer {
|
||||
transition: all 200ms ease;
|
||||
}
|
||||
|
||||
/* Custom styles for the waiting screen */
|
||||
.embed--WaitingForTurn {
|
||||
background-color: #f9fafb;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* Responsive adjustments for the document container */
|
||||
@media (min-width: 768px) {
|
||||
.embed--DocumentContainer {
|
||||
gap: 2rem;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Maintain contrast">
|
||||
Ensure sufficient contrast between background and foreground colors for accessibility.
|
||||
</Accordion>
|
||||
<Accordion title="Test dark mode">
|
||||
If dark mode is not disabled, verify your variables work in both modes.
|
||||
</Accordion>
|
||||
<Accordion title="Match your brand">
|
||||
Align `primary` and `accent` colors with your brand for a cohesive look.
|
||||
</Accordion>
|
||||
<Accordion title="Consistent radius">
|
||||
Use a border radius that matches your application's design system.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [React](/docs/developers/embedding/sdks/react) - React SDK usage
|
||||
- [Vue](/docs/developers/embedding/sdks/vue) - Vue SDK usage
|
||||
@@ -0,0 +1,514 @@
|
||||
---
|
||||
title: Direct Links
|
||||
description: Share a URL or embed an iframe to let users sign documents without email invitations.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## What Are Direct Links?
|
||||
|
||||
Direct links are unique URLs tied to a template that allow anyone to:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
View and sign a document without receiving an email invitation
|
||||
</Step>
|
||||
<Step>
|
||||
Enter their own name and email address
|
||||
</Step>
|
||||
<Step>
|
||||
Complete signature fields and submit the document
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
When someone uses a direct link, Documenso creates a new document from the template with that person as the signer.
|
||||
|
||||
### When to Use Direct Links
|
||||
|
||||
- Collecting signatures from unknown recipients (forms, waivers, petitions)
|
||||
- Embedding signing in your website or application
|
||||
- Self-service contracts where customers initiate signing
|
||||
- Public-facing agreements that anyone can sign
|
||||
|
||||
### Limitations
|
||||
|
||||
- Only work with templates, not individual documents
|
||||
- Each link is tied to one recipient role in the template
|
||||
- Recipients enter their own information (you cannot prefill recipient details)
|
||||
|
||||
## Creating Direct Link Templates
|
||||
|
||||
Before embedding, you need a template with direct links enabled.
|
||||
|
||||
### Via the Dashboard
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Go to **Templates** and create or select a template
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
Click the three-dot menu and select **Direct link**
|
||||
</Step>
|
||||
<Step>
|
||||
Click **Enable direct link signing**
|
||||
</Step>
|
||||
<Step>
|
||||
Choose which recipient in your template will use the direct link
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
Copy the generated URL
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Via the API
|
||||
|
||||
Create a direct link for an existing template:
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
# Create direct link for template
|
||||
curl -X POST "https://app.documenso.com/api/v2/template/direct/create" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"templateId": 123,
|
||||
"directRecipientId": 1
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const API_TOKEN = process.env.DOCUMENSO_API_TOKEN;
|
||||
const BASE_URL = 'https://app.documenso.com/api/v2';
|
||||
|
||||
const response = await fetch(`${BASE_URL}/template/direct/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId: 123,
|
||||
directRecipientId: 1, // Optional: specific recipient to use
|
||||
}),
|
||||
});
|
||||
|
||||
const directLink = await response.json();
|
||||
console.log('Direct link token:', directLink.token);
|
||||
// URL: https://app.documenso.com/d/{token}
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 456,
|
||||
"token": "abc123xyz",
|
||||
"templateId": 123,
|
||||
"directTemplateRecipientId": 1,
|
||||
"enabled": true,
|
||||
"createdAt": "2025-01-15T10:00:00.000Z"
|
||||
}
|
||||
````
|
||||
|
||||
The direct link URL format is:
|
||||
|
||||
```
|
||||
https://app.documenso.com/d/{token}
|
||||
```
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Embedding in an iframe
|
||||
|
||||
Embed the signing experience directly in your application using an iframe.
|
||||
|
||||
### Basic iframe Embedding
|
||||
|
||||
```html
|
||||
<iframe
|
||||
src="https://app.documenso.com/embed/direct/abc123xyz"
|
||||
width="100%"
|
||||
height="800"
|
||||
frameborder="0"
|
||||
allow="clipboard-write"
|
||||
></iframe>
|
||||
```
|
||||
|
||||
### Responsive iframe
|
||||
|
||||
```html
|
||||
<div style="position: relative; width: 100%; padding-bottom: 75%; height: 0; overflow: hidden;">
|
||||
<iframe
|
||||
src="https://app.documenso.com/embed/direct/abc123xyz"
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none;"
|
||||
allow="clipboard-write"
|
||||
></iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
### React Component Example
|
||||
|
||||
```tsx
|
||||
function DocumentSigner({ token }: { token: string }) {
|
||||
return (
|
||||
<div className="h-[800px] w-full">
|
||||
<iframe
|
||||
src={`https://app.documenso.com/embed/direct/${token}`}
|
||||
className="h-full w-full border-0"
|
||||
allow="clipboard-write"
|
||||
title="Sign Document"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
The embed URL uses `/embed/direct/{token}` instead of `/d/{token}`. The embed version is optimized
|
||||
for iframe embedding with reduced navigation.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Embedding with Redirect
|
||||
|
||||
Instead of an iframe, redirect users to the signing page and bring them back after completion.
|
||||
|
||||
### Simple Redirect
|
||||
|
||||
```typescript
|
||||
function redirectToSigning(token: string) {
|
||||
window.location.href = `https://app.documenso.com/d/${token}`;
|
||||
}
|
||||
```
|
||||
|
||||
### With Return URL
|
||||
|
||||
Configure a redirect URL in your template settings to return users to your application after signing:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Edit your template
|
||||
</Step>
|
||||
<Step>
|
||||
Go to **Advanced Settings**
|
||||
</Step>
|
||||
<Step>
|
||||
Set **Redirect URL** to your desired return URL (e.g., `https://yourapp.com/signed`)
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Or set it via API when creating the template:
|
||||
|
||||
```typescript
|
||||
const response = await fetch(`${BASE_URL}/template/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId: 123,
|
||||
meta: {
|
||||
redirectUrl: 'https://yourapp.com/signing-complete',
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
After signing completes, users are automatically redirected to the specified URL.
|
||||
|
||||
---
|
||||
|
||||
## URL Parameters
|
||||
|
||||
Pass additional data through the direct link URL using query parameters.
|
||||
|
||||
### External ID
|
||||
|
||||
Track which document belongs to which transaction in your system:
|
||||
|
||||
```
|
||||
https://app.documenso.com/d/abc123xyz?externalId=order-12345
|
||||
```
|
||||
|
||||
The external ID is stored with the created document and included in webhook payloads.
|
||||
|
||||
### Example: Dynamic External ID
|
||||
|
||||
```typescript
|
||||
function getSigningUrl(token: string, orderId: string) {
|
||||
const params = new URLSearchParams({
|
||||
externalId: orderId,
|
||||
});
|
||||
|
||||
return `https://app.documenso.com/d/${token}?${params.toString()}`;
|
||||
}
|
||||
|
||||
// Usage
|
||||
const url = getSigningUrl('abc123xyz', 'order-12345');
|
||||
// https://app.documenso.com/d/abc123xyz?externalId=order-12345
|
||||
```
|
||||
|
||||
### Embed URL Parameters
|
||||
|
||||
The embed URL supports the same parameters:
|
||||
|
||||
```
|
||||
https://app.documenso.com/embed/direct/abc123xyz?externalId=order-12345
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Access Authentication
|
||||
|
||||
Direct links can require authentication before signing. Configure this in your template settings:
|
||||
|
||||
| Auth Type | Description |
|
||||
| --------- | ---------------------------------------------- |
|
||||
| None | Anyone with the link can sign |
|
||||
| Account | Signer must be logged into a Documenso account |
|
||||
|
||||
To require authentication, set `globalAccessAuth` when creating or updating the template:
|
||||
|
||||
```typescript
|
||||
const response = await fetch(`${BASE_URL}/template/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId: 123,
|
||||
data: {
|
||||
globalAccessAuth: ['ACCOUNT'], // Require login
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### Disabling Direct Links
|
||||
|
||||
Temporarily disable a direct link without deleting it:
|
||||
|
||||
<Tabs items={['curl', 'TypeScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/template/direct/toggle" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"templateId": 123,
|
||||
"enabled": false
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="TypeScript">
|
||||
```typescript
|
||||
const response = await fetch(`${BASE_URL}/template/direct/toggle`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId: 123,
|
||||
enabled: false,
|
||||
}),
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
Re-enable later by setting `enabled: true`. The URL remains the same.
|
||||
|
||||
### Deleting Direct Links
|
||||
|
||||
Permanently remove a direct link:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/template/direct/delete" \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"templateId": 123
|
||||
}'
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
Deleting a direct link invalidates the URL permanently. If you enable direct links again, a new
|
||||
URL will be generated.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Styling and Customization
|
||||
|
||||
### Branding
|
||||
|
||||
The signing experience uses your organisation's branding settings:
|
||||
|
||||
- Logo
|
||||
- Primary color
|
||||
- Email customization
|
||||
|
||||
Configure branding in **Settings** > **Branding** or via team settings.
|
||||
|
||||
### Signature Options
|
||||
|
||||
Control which signature input methods are available:
|
||||
|
||||
```typescript
|
||||
const response = await fetch(`${BASE_URL}/template/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId: 123,
|
||||
meta: {
|
||||
typedSignatureEnabled: true, // Allow typed signatures
|
||||
drawSignatureEnabled: true, // Allow drawn signatures
|
||||
uploadSignatureEnabled: false, // Disable uploaded signatures
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### White-labeling
|
||||
|
||||
Enterprise plans support white-label embedding with the "Powered by Documenso" badge removed. [Contact sales](mailto:support@documenso.com) for details.
|
||||
|
||||
---
|
||||
|
||||
## Handling Completion
|
||||
|
||||
### Webhook Notifications
|
||||
|
||||
Set up webhooks to receive notifications when documents are signed:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_SIGNED",
|
||||
"payload": {
|
||||
"id": 123,
|
||||
"externalId": "order-12345",
|
||||
"title": "contract.pdf",
|
||||
"status": "COMPLETED",
|
||||
"Recipient": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"signingStatus": "SIGNED",
|
||||
"signedAt": "2024-01-15T10:30:00.000Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-01-15T10:30:00.000Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
See [Webhooks](/docs/developers/webhooks) for setup instructions.
|
||||
|
||||
### Polling for Status
|
||||
|
||||
If webhooks aren't suitable, poll the document status:
|
||||
|
||||
```typescript
|
||||
async function checkDocumentStatus(externalId: string) {
|
||||
const response = await fetch(`${BASE_URL}/envelope?source=TEMPLATE_DIRECT_LINK`, {
|
||||
headers: { Authorization: API_TOKEN },
|
||||
});
|
||||
|
||||
const { data } = await response.json();
|
||||
|
||||
return data.find((doc) => doc.externalId === externalId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
This example shows a complete workflow for embedding direct link signing:
|
||||
|
||||
```typescript
|
||||
const API_TOKEN = process.env.DOCUMENSO_API_TOKEN;
|
||||
const BASE_URL = 'https://app.documenso.com/api/v2';
|
||||
|
||||
// 1. Get template with direct link
|
||||
async function getDirectLinkTemplate(templateId: number) {
|
||||
const response = await fetch(`${BASE_URL}/template/${templateId}`, {
|
||||
headers: { Authorization: API_TOKEN },
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// 2. Create direct link if not exists
|
||||
async function ensureDirectLink(templateId: number, recipientId: number) {
|
||||
const template = await getDirectLinkTemplate(templateId);
|
||||
|
||||
if (template.directLink?.enabled) {
|
||||
return template.directLink.token;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/template/direct/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId,
|
||||
directRecipientId: recipientId,
|
||||
}),
|
||||
});
|
||||
|
||||
const directLink = await response.json();
|
||||
return directLink.token;
|
||||
}
|
||||
|
||||
// 3. Generate signing URL with tracking
|
||||
function getEmbedUrl(token: string, orderId: string) {
|
||||
const params = new URLSearchParams({ externalId: orderId });
|
||||
return `https://app.documenso.com/embed/direct/${token}?${params}`;
|
||||
}
|
||||
|
||||
// Usage
|
||||
const token = await ensureDirectLink(123, 1);
|
||||
const embedUrl = getEmbedUrl(token, 'order-12345');
|
||||
|
||||
// Embed in your page
|
||||
document.getElementById('signer-frame').src = embedUrl;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [SDKs](/docs/developers/embedding/sdks) - Framework SDK integration
|
||||
- [Templates API](/docs/developers/api/templates) - Template management
|
||||
- [Webhooks](/docs/developers/webhooks) - Event notifications
|
||||
- [Direct Links (User Guide)](/docs/users/documents/direct-links) - End-user documentation
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
title: Embedding
|
||||
description: Embed document signing experiences directly in your application using official SDKs.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Embedded Signing vs Embedded Authoring
|
||||
|
||||
Documenso offers two types of embedding:
|
||||
|
||||
- **Embedded Signing** lets you embed the signing experience in your application. Your users sign documents without leaving your site. Available on Teams Plan and above.
|
||||
- **Embedded Authoring** lets you embed document and template _creation and editing_ in your application. This is an [Enterprise](/docs/policies/enterprise-edition) feature (also available as a Platform Plan add-on). See the [Authoring](/docs/developers/embedding/authoring) guide.
|
||||
|
||||
This page covers **embedded signing**. If you need your users to create or edit documents inside your app, see [Authoring](/docs/developers/embedding/authoring).
|
||||
|
||||
---
|
||||
|
||||
## Availability
|
||||
|
||||
Embedding is available on **Teams Plan** and above, as well as for **Early Adopters** within a team (Early Adopters can create a team for free).
|
||||
|
||||
The [Platform Plan](https://documen.so/platform-cta-pricing) adds enhanced customization:
|
||||
|
||||
- Custom CSS and styling variables
|
||||
- Dark mode controls
|
||||
- Removal of Documenso branding
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
There are two ways to embed signing, each using a different component and token type.
|
||||
|
||||
### Direct Templates
|
||||
|
||||
Direct templates are evergreen - each time a user completes signing, a new document is created from the template. This is the recommended approach for most use cases.
|
||||
|
||||
Use the `EmbedDirectTemplate` component with a template token:
|
||||
|
||||
```jsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-react';
|
||||
|
||||
<EmbedDirectTemplate
|
||||
token="your-template-token"
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
/>;
|
||||
```
|
||||
|
||||
### Signing Tokens
|
||||
|
||||
For advanced integrations where you create documents via the API, you can embed the signing experience for a specific recipient using their signing token.
|
||||
|
||||
Use the `EmbedSignDocument` component with the recipient's token:
|
||||
|
||||
```jsx
|
||||
import { EmbedSignDocument } from '@documenso/embed-react';
|
||||
|
||||
<EmbedSignDocument
|
||||
token="recipient-signing-token"
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
/>;
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
For most use cases, direct templates are the way to go. Use signing tokens when you need
|
||||
programmatic control over document creation via the API.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Getting Your Token
|
||||
|
||||
### Direct Template Token
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Navigate to your team's templates in Documenso
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
Click on a direct link template to copy its URL. The token is the last segment of the URL.
|
||||
|
||||
For example, `https://app.documenso.com/d/-WoSwWVT-fYOERS2MI37k` has the token `-WoSwWVT-fYOERS2MI37k`.
|
||||
|
||||
If your template is not a direct link template yet, select **Direct Link** from the three-dot menu on the templates table to enable it.
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Signing Token
|
||||
|
||||
Signing tokens are returned in API responses when distributing a document. You can also get one manually by hovering over a recipient's avatar on a document you own and clicking their email.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Framework SDKs
|
||||
|
||||
Pick your framework to get started:
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="React"
|
||||
description="@documenso/embed-react"
|
||||
href="/docs/developers/embedding/sdks/react"
|
||||
/>
|
||||
<Card title="Vue" description="@documenso/embed-vue" href="/docs/developers/embedding/sdks/vue" />
|
||||
<Card
|
||||
title="Svelte"
|
||||
description="@documenso/embed-svelte"
|
||||
href="/docs/developers/embedding/sdks/svelte"
|
||||
/>
|
||||
<Card
|
||||
title="Angular"
|
||||
description="@documenso/embed-angular"
|
||||
href="/docs/developers/embedding/sdks/angular"
|
||||
/>
|
||||
<Card
|
||||
title="Solid"
|
||||
description="@documenso/embed-solid"
|
||||
href="/docs/developers/embedding/sdks/solid"
|
||||
/>
|
||||
<Card
|
||||
title="Preact"
|
||||
description="@documenso/embed-preact"
|
||||
href="/docs/developers/embedding/sdks/preact"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
A [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) SDK (`@documenso/embed-webcomponent`) is also available for use outside of JavaScript frameworks. It works in any environment that supports custom elements.
|
||||
|
||||
If you prefer not to use any SDK, you can embed signing using [Direct Links](/docs/developers/embedding/direct-links) with a plain iframe or redirect.
|
||||
|
||||
---
|
||||
|
||||
## Props
|
||||
|
||||
### EmbedDirectTemplate
|
||||
|
||||
| Prop | Type | Description |
|
||||
| --------------------- | ---------- | ---------------------------------------------------------------- |
|
||||
| `token` | `string` | **Required.** The direct template token. |
|
||||
| `host` | `string` | Documenso instance URL. Defaults to `https://app.documenso.com`. |
|
||||
| `name` | `string` | Pre-fill the signer's name. |
|
||||
| `lockName` | `boolean` | Prevent the signer from changing their name. |
|
||||
| `email` | `string` | Pre-fill the signer's email. |
|
||||
| `lockEmail` | `boolean` | Prevent the signer from changing their email. |
|
||||
| `externalId` | `string` | Your reference ID, stored with the created document. |
|
||||
| `css` | `string` | Custom CSS string (Platform Plan). |
|
||||
| `cssVars` | `object` | CSS variable overrides for theming (Platform Plan). |
|
||||
| `darkModeDisabled` | `boolean` | Disable dark mode in the embed (Platform Plan). |
|
||||
| `onDocumentReady` | `function` | Called when the document is loaded and ready. |
|
||||
| `onDocumentCompleted` | `function` | Called when signing is completed. |
|
||||
| `onDocumentError` | `function` | Called when an error occurs. |
|
||||
| `onFieldSigned` | `function` | Called when a field is signed. |
|
||||
| `onFieldUnsigned` | `function` | Called when a field value is cleared. |
|
||||
|
||||
### EmbedSignDocument
|
||||
|
||||
| Prop | Type | Description |
|
||||
| --------------------- | ---------- | ---------------------------------------------------------------- |
|
||||
| `token` | `string` | **Required.** The recipient's signing token. |
|
||||
| `host` | `string` | Documenso instance URL. Defaults to `https://app.documenso.com`. |
|
||||
| `name` | `string` | Pre-fill the signer's name. |
|
||||
| `lockName` | `boolean` | Prevent the signer from changing their name. |
|
||||
| `onDocumentReady` | `function` | Called when the document is loaded and ready. |
|
||||
| `onDocumentCompleted` | `function` | Called when signing is completed. |
|
||||
| `onDocumentError` | `function` | Called when an error occurs. |
|
||||
|
||||
---
|
||||
|
||||
## Event Callbacks
|
||||
|
||||
### onDocumentCompleted
|
||||
|
||||
Receives an object with:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | -------- | ------------------------------ |
|
||||
| `token` | `string` | The token used for signing. |
|
||||
| `documentId` | `number` | The ID of the signed document. |
|
||||
| `recipientId` | `number` | The ID of the recipient. |
|
||||
|
||||
### onFieldSigned
|
||||
|
||||
Receives an object with:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ---------- | --------- | -------------------------------------------- |
|
||||
| `fieldId` | `number` | The ID of the field. |
|
||||
| `value` | `string` | The field value. |
|
||||
| `isBase64` | `boolean` | Whether the value is a base64 encoded image. |
|
||||
|
||||
### onFieldUnsigned
|
||||
|
||||
Receives an object with:
|
||||
|
||||
| Field | Type | Description |
|
||||
| --------- | -------- | -------------------- |
|
||||
| `fieldId` | `number` | The ID of the field. |
|
||||
|
||||
---
|
||||
|
||||
## More
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Direct Links"
|
||||
description="Embed with iframes or redirects, no SDK required."
|
||||
href="/docs/developers/embedding/direct-links"
|
||||
/>
|
||||
<Card
|
||||
title="CSS Variables"
|
||||
description="Customize colors, spacing, and theming."
|
||||
href="/docs/developers/embedding/css-variables"
|
||||
/>
|
||||
<Card
|
||||
title="Authoring"
|
||||
description="Embed document and template creation."
|
||||
href="/docs/developers/embedding/authoring"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Documents API](/docs/developers/api/documents) - Create documents programmatically
|
||||
- [Templates API](/docs/developers/api/templates) - Manage templates via API
|
||||
- [Webhooks](/docs/developers/webhooks) - Receive server-side signing notifications
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Embedding",
|
||||
"pages": ["sdks", "direct-links", "css-variables", "authoring"]
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Angular
|
||||
description: Embed Documenso signing in your Angular application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-angular ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-angular ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-angular ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-signing',
|
||||
standalone: true,
|
||||
imports: [EmbedDirectTemplate],
|
||||
template: `
|
||||
<embed-direct-template
|
||||
[token]="token"
|
||||
[name]="'John Doe'"
|
||||
[email]="'john@example.com'"
|
||||
[lockEmail]="true"
|
||||
[externalId]="'order-12345'"
|
||||
(documentReady)="onReady()"
|
||||
(documentCompleted)="onCompleted($event)"
|
||||
(documentError)="onError()"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class SigningComponent {
|
||||
token = 'your-template-token';
|
||||
|
||||
onReady() {
|
||||
console.log('Ready');
|
||||
}
|
||||
|
||||
onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
|
||||
onError() {
|
||||
console.error('Error');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```typescript
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { EmbedSignDocument } from '@documenso/embed-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-signing',
|
||||
standalone: true,
|
||||
imports: [EmbedSignDocument],
|
||||
template: `
|
||||
<embed-sign-document
|
||||
[token]="token"
|
||||
(documentCompleted)="onCompleted($event)"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class SigningComponent {
|
||||
@Input() token = '';
|
||||
|
||||
onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: SDKs
|
||||
description: Official embedding SDKs for React, Vue, Svelte, Angular, Solid, and Preact.
|
||||
---
|
||||
|
||||
Install the SDK for your framework and embed document signing with a few lines of code.
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="React"
|
||||
description="@documenso/embed-react"
|
||||
href="/docs/developers/embedding/sdks/react"
|
||||
/>
|
||||
<Card title="Vue" description="@documenso/embed-vue" href="/docs/developers/embedding/sdks/vue" />
|
||||
<Card
|
||||
title="Svelte"
|
||||
description="@documenso/embed-svelte"
|
||||
href="/docs/developers/embedding/sdks/svelte"
|
||||
/>
|
||||
<Card
|
||||
title="Angular"
|
||||
description="@documenso/embed-angular"
|
||||
href="/docs/developers/embedding/sdks/angular"
|
||||
/>
|
||||
<Card
|
||||
title="Solid"
|
||||
description="@documenso/embed-solid"
|
||||
href="/docs/developers/embedding/sdks/solid"
|
||||
/>
|
||||
<Card
|
||||
title="Preact"
|
||||
description="@documenso/embed-preact"
|
||||
href="/docs/developers/embedding/sdks/preact"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
If you are not using a JavaScript framework, you can embed signing using [Direct Links](/docs/developers/embedding/direct-links) with a plain iframe or redirect.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "SDKs",
|
||||
"pages": ["react", "vue", "svelte", "angular", "solid", "preact"]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Preact
|
||||
description: Embed Documenso signing in your Preact application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-preact ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-preact ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-preact ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-preact';
|
||||
|
||||
const SigningPage = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-template-token"
|
||||
name="John Doe"
|
||||
email="john@example.com"
|
||||
lockEmail={true}
|
||||
externalId="order-12345"
|
||||
onDocumentReady={() => console.log('Ready')}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
onDocumentError={() => console.error('Error')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```tsx
|
||||
import { EmbedSignDocument } from '@documenso/embed-preact';
|
||||
|
||||
const SigningPage = ({ token }: { token: string }) => {
|
||||
return (
|
||||
<EmbedSignDocument
|
||||
token={token}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling (Platform Plan)
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-preact';
|
||||
|
||||
const StyledEmbed = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-token"
|
||||
css={`
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`}
|
||||
cssVars={{
|
||||
primary: '#0000FF',
|
||||
background: '#F5F5F5',
|
||||
radius: '8px',
|
||||
}}
|
||||
darkModeDisabled={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
See [CSS Variables](/docs/developers/embedding/css-variables) for all available variables.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: React
|
||||
description: Embed Documenso signing in your React application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-react ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-react ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-react ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-react';
|
||||
|
||||
const SigningPage = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-template-token"
|
||||
name="John Doe"
|
||||
email="john@example.com"
|
||||
lockEmail={true}
|
||||
externalId="order-12345"
|
||||
onDocumentReady={() => console.log('Ready')}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
onDocumentError={() => console.error('Error')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```tsx
|
||||
import { EmbedSignDocument } from '@documenso/embed-react';
|
||||
|
||||
const SigningPage = ({ token }: { token: string }) => {
|
||||
return (
|
||||
<EmbedSignDocument
|
||||
token={token}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling (Platform Plan)
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-react';
|
||||
|
||||
const StyledEmbed = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-token"
|
||||
css={`
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`}
|
||||
cssVars={{
|
||||
primary: '#0000FF',
|
||||
background: '#F5F5F5',
|
||||
radius: '8px',
|
||||
}}
|
||||
darkModeDisabled={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
See [CSS Variables](/docs/developers/embedding/css-variables) for all available variables.
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-react';
|
||||
|
||||
type Status = 'loading' | 'ready' | 'completed' | 'error';
|
||||
|
||||
const DocumentSigning = ({ token }: { token: string }) => {
|
||||
const [status, setStatus] = useState<Status>('loading');
|
||||
|
||||
if (status === 'completed') {
|
||||
return <p>Thank you for signing the document.</p>;
|
||||
}
|
||||
|
||||
if (status === 'error') {
|
||||
return <p>An error occurred. Please try again.</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', height: '100vh' }}>
|
||||
{status === 'loading' && (
|
||||
<div style={{ position: 'absolute', inset: 0, display: 'grid', placeItems: 'center' }}>
|
||||
Loading...
|
||||
</div>
|
||||
)}
|
||||
<EmbedDirectTemplate
|
||||
token={token}
|
||||
onDocumentReady={() => setStatus('ready')}
|
||||
onDocumentCompleted={() => setStatus('completed')}
|
||||
onDocumentError={() => setStatus('error')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Solid
|
||||
description: Embed Documenso signing in your Solid application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-solid ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-solid ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-solid ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-solid';
|
||||
|
||||
const SigningPage = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-template-token"
|
||||
name="John Doe"
|
||||
email="john@example.com"
|
||||
lockEmail={true}
|
||||
externalId="order-12345"
|
||||
onDocumentReady={() => console.log('Ready')}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
onDocumentError={() => console.error('Error')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```tsx
|
||||
import { EmbedSignDocument } from '@documenso/embed-solid';
|
||||
|
||||
const SigningPage = (props: { token: string }) => {
|
||||
return (
|
||||
<EmbedSignDocument
|
||||
token={props.token}
|
||||
onDocumentCompleted={(data) => {
|
||||
console.log('Signed:', data.documentId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling (Platform Plan)
|
||||
|
||||
```tsx
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-solid';
|
||||
|
||||
const StyledEmbed = () => {
|
||||
return (
|
||||
<EmbedDirectTemplate
|
||||
token="your-token"
|
||||
css={`
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`}
|
||||
cssVars={{
|
||||
primary: '#0000FF',
|
||||
background: '#F5F5F5',
|
||||
radius: '8px',
|
||||
}}
|
||||
darkModeDisabled={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
See [CSS Variables](/docs/developers/embedding/css-variables) for all available variables.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Svelte
|
||||
description: Embed Documenso signing in your Svelte application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-svelte ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-svelte ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-svelte ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-svelte';
|
||||
|
||||
const token = 'your-template-token';
|
||||
|
||||
function onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<EmbedDirectTemplate
|
||||
{token}
|
||||
name="John Doe"
|
||||
email="john@example.com"
|
||||
lockEmail={true}
|
||||
externalId="order-12345"
|
||||
onDocumentReady={() => console.log('Ready')}
|
||||
onDocumentCompleted={onCompleted}
|
||||
onDocumentError={() => console.error('Error')}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import { EmbedSignDocument } from '@documenso/embed-svelte';
|
||||
|
||||
export let token: string;
|
||||
|
||||
function onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<EmbedSignDocument
|
||||
{token}
|
||||
onDocumentCompleted={onCompleted}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling (Platform Plan)
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-svelte';
|
||||
|
||||
const token = 'your-token';
|
||||
|
||||
const customCss = `
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`;
|
||||
|
||||
const cssVars = {
|
||||
primary: '#0000FF',
|
||||
background: '#F5F5F5',
|
||||
radius: '8px',
|
||||
};
|
||||
</script>
|
||||
|
||||
<EmbedDirectTemplate
|
||||
{token}
|
||||
css={customCss}
|
||||
cssVars={cssVars}
|
||||
darkModeDisabled={true}
|
||||
/>
|
||||
```
|
||||
|
||||
See [CSS Variables](/docs/developers/embedding/css-variables) for all available variables.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Vue
|
||||
description: Embed Documenso signing in your Vue application using the official SDK.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs items={['npm', 'yarn', 'pnpm']}>
|
||||
<Tab value="npm">``` npm install @documenso/embed-vue ```</Tab>
|
||||
<Tab value="yarn">``` yarn add @documenso/embed-vue ```</Tab>
|
||||
<Tab value="pnpm">``` pnpm add @documenso/embed-vue ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Direct Template
|
||||
|
||||
```html
|
||||
<script setup lang="ts">
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-vue';
|
||||
|
||||
const token = 'your-template-token';
|
||||
|
||||
function onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EmbedDirectTemplate
|
||||
:token="token"
|
||||
name="John Doe"
|
||||
email="john@example.com"
|
||||
:lockEmail="true"
|
||||
externalId="order-12345"
|
||||
@document-completed="onCompleted"
|
||||
@document-ready="() => console.log('Ready')"
|
||||
@document-error="() => console.error('Error')"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Signing Token
|
||||
|
||||
```html
|
||||
<script setup lang="ts">
|
||||
import { EmbedSignDocument } from '@documenso/embed-vue';
|
||||
|
||||
const props = defineProps<{ token: string }>();
|
||||
|
||||
function onCompleted(data: { documentId: number }) {
|
||||
console.log('Signed:', data.documentId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EmbedSignDocument :token="props.token" @document-completed="onCompleted" />
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling (Platform Plan)
|
||||
|
||||
```html
|
||||
<script setup lang="ts">
|
||||
import { EmbedDirectTemplate } from '@documenso/embed-vue';
|
||||
|
||||
const token = 'your-token';
|
||||
|
||||
const customCss = `
|
||||
.documenso-embed {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
`;
|
||||
|
||||
const cssVars = {
|
||||
primary: '#0000FF',
|
||||
background: '#F5F5F5',
|
||||
radius: '8px',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EmbedDirectTemplate
|
||||
:token="token"
|
||||
:css="customCss"
|
||||
:cssVars="cssVars"
|
||||
:darkModeDisabled="true"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
See [CSS Variables](/docs/developers/embedding/css-variables) for all available variables.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Embedding Overview](/docs/developers/embedding) - Props reference and concepts
|
||||
- [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance
|
||||
- [Authoring](/docs/developers/embedding/authoring) - Embed document creation
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Examples
|
||||
description: Common integration patterns and end-to-end workflows.
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Common Workflows"
|
||||
description="End-to-end examples for typical use cases."
|
||||
href="/docs/developers/examples/common-workflows"
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Examples",
|
||||
"pages": ["common-workflows"]
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Authentication
|
||||
description: Generate an API key and authenticate your requests.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Documenso account (cloud or self-hosted)
|
||||
- A Documenso account on any plan (Free, Individual, Team, or Enterprise)
|
||||
|
||||
<Callout type="info">
|
||||
Free accounts include API access with a limit of 5 documents per month. [Upgrade to a paid
|
||||
plan](https://documen.so/pricing) for higher limits.
|
||||
</Callout>
|
||||
|
||||
## Create an API Token
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Open settings
|
||||
|
||||
- Log in to your Documenso account
|
||||
- Click your avatar in the top right corner
|
||||
- Select **Settings** from the dropdown menu
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Navigate to the API Tokens tab
|
||||
|
||||
Go to **Settings** and open the **API Tokens** tab.
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Generate a new token
|
||||
|
||||
- Click **Create Token**
|
||||
- Enter a descriptive name (e.g., `production-backend`, `zapier-integration`)
|
||||
- Select an expiration period: never expires, 7 days, 1 month, 3 months, 6 months, or 1 year
|
||||
- Click **Create Token**
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Copy your token
|
||||
|
||||
Your token is displayed once after creation. Copy it immediately and store it securely.
|
||||
|
||||

|
||||
|
||||
<Callout type="warn">
|
||||
You cannot view the token again after leaving this page. If you lose it, you must create a new
|
||||
token.
|
||||
</Callout>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Using Your Token
|
||||
|
||||
Include the token in the `Authorization` header of your HTTP requests.
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl https://app.documenso.com/api/v2/documents \
|
||||
-H "Authorization: api_xxxxxxxxxxxxxxxx"
|
||||
```
|
||||
|
||||
### JavaScript / TypeScript
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/documents', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: 'api_xxxxxxxxxxxxxxxx',
|
||||
},
|
||||
});
|
||||
|
||||
const documents = await response.json();
|
||||
```
|
||||
|
||||
### Using the TypeScript SDK
|
||||
|
||||
Documenso provides official SDKs that handle authentication for you:
|
||||
|
||||
```typescript
|
||||
import { Documenso } from '@documenso/sdk-typescript';
|
||||
|
||||
const client = new Documenso({
|
||||
apiKey: 'api_xxxxxxxxxxxxxxxx',
|
||||
});
|
||||
|
||||
const documents = await client.documents.find();
|
||||
```
|
||||
|
||||
SDKs are available for [TypeScript](https://github.com/documenso/sdk-typescript), [Python](https://github.com/documenso/sdk-python), and [Go](https://github.com/documenso/sdk-go).
|
||||
|
||||
## API Base URLs
|
||||
|
||||
| Environment | Base URL |
|
||||
| ----------- | -------------------------------------- |
|
||||
| Production | `https://app.documenso.com/api/v2` |
|
||||
| Staging | `https://stg-app.documenso.com/api/v2` |
|
||||
| Self-hosted | `https://your-domain.com/api/v2` |
|
||||
|
||||
<Callout type="info">
|
||||
API V1 is deprecated. Use V2 for all new integrations. V1 only works with legacy documents created
|
||||
before the envelope system. If you need V1 documentation for migration purposes, see the [V1
|
||||
OpenAPI reference](https://app.documenso.com/api/v1/openapi).
|
||||
</Callout>
|
||||
|
||||
<Callout type="info">
|
||||
The API is available on all plans, including Free (5 documents per month). [Fair
|
||||
Use](/docs/policies/fair-use) applies to all API usage.
|
||||
</Callout>
|
||||
|
||||
## Token Security
|
||||
|
||||
API tokens grant full access to your account. Follow these practices to keep them secure:
|
||||
|
||||
- **Never commit tokens to version control.** Use environment variables instead.
|
||||
- **Use descriptive names.** Names like `zapier-prod` or `backend-staging` help you identify token usage.
|
||||
- **Set expiration dates.** Shorter expiration periods reduce risk if a token is compromised.
|
||||
- **Rotate tokens regularly.** Create new tokens and revoke old ones periodically.
|
||||
- **Use separate tokens per integration.** If one is compromised, you only need to revoke that specific token.
|
||||
- **Revoke unused tokens.** Delete tokens you no longer need from the API Tokens settings page.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Store your token in an environment variable rather than hardcoding it:
|
||||
|
||||
```bash
|
||||
# .env (do not commit this file)
|
||||
DOCUMENSO_API_KEY=api_xxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
```typescript
|
||||
const client = new Documenso({
|
||||
apiKey: process.env.DOCUMENSO_API_KEY,
|
||||
});
|
||||
```
|
||||
|
||||
## Token Scope
|
||||
|
||||
API tokens have full access to your account, including:
|
||||
|
||||
- Creating, reading, updating, and deleting documents
|
||||
- Managing recipients and fields
|
||||
- Accessing templates
|
||||
- Managing team resources (if the token owner has team access)
|
||||
|
||||
There is currently no way to create tokens with limited scopes or permissions.
|
||||
|
||||
## Revoking a Token
|
||||
|
||||
To revoke a token:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Go to **Settings** > **API Tokens**
|
||||
</Step>
|
||||
<Step>
|
||||
Find the token you want to revoke
|
||||
</Step>
|
||||
<Step>
|
||||
Click the delete icon next to the token
|
||||
</Step>
|
||||
<Step>
|
||||
Confirm the deletion
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Revoked tokens stop working immediately. Any integrations using that token will receive `401 Unauthorized` errors.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="401 Unauthorized — Missing or invalid token">
|
||||
Check that you included the token in the `Authorization` header.
|
||||
</Accordion>
|
||||
<Accordion title="401 Unauthorized — Expired token">Create a new token in settings.</Accordion>
|
||||
<Accordion title="403 Forbidden — Token doesn't have access to the resource">
|
||||
Ensure you're accessing resources owned by the token's account.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Make your first API call](/docs/developers/getting-started/first-api-call) - Create a document via the API
|
||||
- [API Reference](/docs/developers/api) - Explore available endpoints
|
||||
@@ -0,0 +1,519 @@
|
||||
---
|
||||
title: First API Call
|
||||
description: Create and send a document for signing using the Documenso API, from uploading a PDF to adding recipients and distributing for signature.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, you need:
|
||||
|
||||
- A Documenso account (cloud or self-hosted)
|
||||
- An API token ([create one in team settings](/docs/developers/getting-started/authentication))
|
||||
- A PDF file to send for signing
|
||||
|
||||
<Callout type="warn">
|
||||
API tokens have full access to your account. Store them securely and never commit them to version
|
||||
control.
|
||||
</Callout>
|
||||
|
||||
## Limitations
|
||||
|
||||
The API cannot:
|
||||
|
||||
- Sign documents on behalf of recipients (recipients must sign themselves)
|
||||
- Convert non-PDF files to PDF (you must upload PDFs)
|
||||
- Retrieve the signed PDF until all recipients have completed signing
|
||||
|
||||
## Base URL
|
||||
|
||||
All API requests use the following base URLs:
|
||||
|
||||
| Environment | Base URL |
|
||||
| ----------- | -------------------------------------- |
|
||||
| Production | `https://app.documenso.com/api/v2` |
|
||||
| Staging | `https://stg-app.documenso.com/api/v2` |
|
||||
|
||||
## Example 1: List Your Documents
|
||||
|
||||
Start with a simple GET request to verify your API token works.
|
||||
|
||||
<Tabs items={['curl', 'JavaScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X GET "https://app.documenso.com/api/v2/envelope" \
|
||||
-H "Authorization: YOUR_API_TOKEN"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="JavaScript">
|
||||
```javascript
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': 'YOUR_API_TOKEN',
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
A successful response returns a list of your documents (envelopes):
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "envelope_abc123",
|
||||
"status": "DRAFT",
|
||||
"title": "Contract Agreement",
|
||||
"createdAt": "2025-01-15T10:30:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"perPage": 10,
|
||||
"totalPages": 1,
|
||||
"totalItems": 1
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
If you receive a `401 Unauthorized` error, verify your API token is correct and includes the `api_` prefix.
|
||||
|
||||
## Example 2: Create a Document with Recipient and Signature Field
|
||||
|
||||
The V2 API uses a single endpoint to create a document with recipients and fields in one request. This is the most common pattern for sending documents.
|
||||
|
||||
<Tabs items={['curl', 'JavaScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
|
||||
-H "Authorization: YOUR_API_TOKEN" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F 'payload={
|
||||
"type": "DOCUMENT",
|
||||
"title": "Service Agreement",
|
||||
"recipients": [
|
||||
{
|
||||
"email": "signer@example.com",
|
||||
"name": "John Smith",
|
||||
"role": "SIGNER",
|
||||
"fields": [
|
||||
{
|
||||
"identifier": 0,
|
||||
"type": "SIGNATURE",
|
||||
"page": 1,
|
||||
"positionX": 10,
|
||||
"positionY": 80,
|
||||
"width": 30,
|
||||
"height": 5
|
||||
},
|
||||
{
|
||||
"identifier": 0,
|
||||
"type": "DATE",
|
||||
"page": 1,
|
||||
"positionX": 50,
|
||||
"positionY": 80,
|
||||
"width": 20,
|
||||
"height": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}' \
|
||||
-F "files=@./contract.pdf;type=application/pdf"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="JavaScript">
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const FormData = require('form-data');
|
||||
|
||||
const form = new FormData();
|
||||
|
||||
const payload = {
|
||||
type: 'DOCUMENT',
|
||||
title: 'Service Agreement',
|
||||
recipients: [
|
||||
{
|
||||
email: 'signer@example.com',
|
||||
name: 'John Smith',
|
||||
role: 'SIGNER',
|
||||
fields: [
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'SIGNATURE',
|
||||
page: 1,
|
||||
positionX: 10,
|
||||
positionY: 80,
|
||||
width: 30,
|
||||
height: 5,
|
||||
},
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'DATE',
|
||||
page: 1,
|
||||
positionX: 50,
|
||||
positionY: 80,
|
||||
width: 20,
|
||||
height: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
form.append('payload', JSON.stringify(payload));
|
||||
form.append('files', fs.createReadStream('./contract.pdf'), {
|
||||
contentType: 'application/pdf',
|
||||
});
|
||||
|
||||
const response = await fetch('https://app.documenso.com/api/v2/envelope/create', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'YOUR_API_TOKEN',
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Created envelope:', data.id);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Understanding Field Positioning
|
||||
|
||||
Field positions use percentage values (0-100) relative to the PDF page dimensions:
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | ----------- |
|
||||
| `positionX` | Horizontal position from left edge (0 = left, 100 = right) |
|
||||
| `positionY` | Vertical position from top edge (0 = top, 100 = bottom) |
|
||||
| `width` | Field width as percentage of page width |
|
||||
| `height` | Field height as percentage of page height |
|
||||
| `page` | Page number (1-indexed) |
|
||||
| `identifier` | Index of the file (0 for first file, 1 for second, etc.) |
|
||||
|
||||
<Callout type="info">
|
||||
To place a signature near the bottom-left of the page, use `positionX: 10` and `positionY: 80`.
|
||||
</Callout>
|
||||
|
||||
### Recipient Roles
|
||||
|
||||
| Role | Description |
|
||||
| ---- | ----------- |
|
||||
| `SIGNER` | Must sign the document |
|
||||
| `APPROVER` | Must approve before signers can sign |
|
||||
| `CC` | Receives a copy but doesn't sign |
|
||||
| `VIEWER` | Can view the document but takes no action |
|
||||
|
||||
<Callout type="info">
|
||||
See the [recipient roles](/docs/concepts/recipient-roles) page for more information.
|
||||
</Callout>
|
||||
|
||||
## Example 3: Send the Document for Signing
|
||||
|
||||
After creating a document, it's in `DRAFT` status. To send it to recipients, use the distribute endpoint:
|
||||
|
||||
<Tabs items={['curl', 'JavaScript']}>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
curl -X POST "https://app.documenso.com/api/v2/envelope/envelope_abc123/distribute" \
|
||||
-H "Authorization: YOUR_API_TOKEN" \
|
||||
-H "Content-Type: application/json"
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="JavaScript">
|
||||
```javascript
|
||||
const envelopeId = 'envelope_abc123';
|
||||
|
||||
const response = await fetch(
|
||||
`https://app.documenso.com/api/v2/envelope/${envelopeId}/distribute`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'YOUR_API_TOKEN',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Document sent:', data);
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
After distribution, recipients receive an email with a link to sign the document. The document status changes from `DRAFT` to `PENDING`.
|
||||
|
||||
## Full Workflow Example
|
||||
|
||||
Here's a complete script that creates and sends a document:
|
||||
|
||||
<Tabs items={['JavaScript', 'curl']}>
|
||||
<Tab value="JavaScript">
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const FormData = require('form-data');
|
||||
|
||||
const API_TOKEN = process.env.DOCUMENSO_API_TOKEN;
|
||||
const BASE_URL = 'https://app.documenso.com/api/v2';
|
||||
|
||||
async function createAndSendDocument(pdfPath, recipientEmail, recipientName) {
|
||||
// Step 1: Create the envelope with recipient and fields
|
||||
const form = new FormData();
|
||||
|
||||
const payload = {
|
||||
type: 'DOCUMENT',
|
||||
title: 'Service Agreement',
|
||||
recipients: [
|
||||
{
|
||||
email: recipientEmail,
|
||||
name: recipientName,
|
||||
role: 'SIGNER',
|
||||
fields: [
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'SIGNATURE',
|
||||
page: 1,
|
||||
positionX: 10,
|
||||
positionY: 80,
|
||||
width: 30,
|
||||
height: 5,
|
||||
},
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'NAME',
|
||||
page: 1,
|
||||
positionX: 10,
|
||||
positionY: 75,
|
||||
width: 30,
|
||||
height: 3,
|
||||
},
|
||||
{
|
||||
identifier: 0,
|
||||
type: 'DATE',
|
||||
page: 1,
|
||||
positionX: 50,
|
||||
positionY: 80,
|
||||
width: 20,
|
||||
height: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
form.append('payload', JSON.stringify(payload));
|
||||
form.append('files', fs.createReadStream(pdfPath), {
|
||||
contentType: 'application/pdf',
|
||||
});
|
||||
|
||||
const createResponse = await fetch(`${BASE_URL}/envelope/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': API_TOKEN,
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
if (!createResponse.ok) {
|
||||
const error = await createResponse.json();
|
||||
throw new Error(`Failed to create envelope: ${JSON.stringify(error)}`);
|
||||
}
|
||||
|
||||
const envelope = await createResponse.json();
|
||||
console.log('Created envelope:', envelope.id);
|
||||
|
||||
// Step 2: Send the document for signing
|
||||
const distributeResponse = await fetch(
|
||||
`${BASE_URL}/envelope/${envelope.id}/distribute`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': API_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!distributeResponse.ok) {
|
||||
const error = await distributeResponse.json();
|
||||
throw new Error(`Failed to distribute envelope: ${JSON.stringify(error)}`);
|
||||
}
|
||||
|
||||
console.log('Document sent for signing!');
|
||||
return envelope.id;
|
||||
}
|
||||
|
||||
// Usage
|
||||
createAndSendDocument(
|
||||
'./contract.pdf',
|
||||
'signer@example.com',
|
||||
'John Smith'
|
||||
).catch(console.error);
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="curl">
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
API_TOKEN="YOUR_API_TOKEN"
|
||||
BASE_URL="https://app.documenso.com/api/v2"
|
||||
PDF_FILE="./contract.pdf"
|
||||
RECIPIENT_EMAIL="signer@example.com"
|
||||
RECIPIENT_NAME="John Smith"
|
||||
|
||||
# Step 1: Create the envelope with recipient and fields
|
||||
|
||||
echo "Creating envelope..."
|
||||
ENVELOPE_RESPONSE=$(curl -s -X POST "${BASE_URL}/envelope/create" \
|
||||
-H "Authorization: ${API_TOKEN}" \
|
||||
-H "Content-Type: multipart/form-data" \
|
||||
-F "payload={
|
||||
\"type\": \"DOCUMENT\",
|
||||
\"title\": \"Service Agreement\",
|
||||
\"recipients\": [
|
||||
{
|
||||
\"email\": \"${RECIPIENT_EMAIL}\",
|
||||
\"name\": \"${RECIPIENT_NAME}\",
|
||||
\"role\": \"SIGNER\",
|
||||
\"fields\": [
|
||||
{
|
||||
\"identifier\": 0,
|
||||
\"type\": \"SIGNATURE\",
|
||||
\"page\": 1,
|
||||
\"positionX\": 10,
|
||||
\"positionY\": 80,
|
||||
\"width\": 30,
|
||||
\"height\": 5
|
||||
},
|
||||
{
|
||||
\"identifier\": 0,
|
||||
\"type\": \"DATE\",
|
||||
\"page\": 1,
|
||||
\"positionX\": 50,
|
||||
\"positionY\": 80,
|
||||
\"width\": 20,
|
||||
\"height\": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}" \
|
||||
-F "files=@${PDF_FILE};type=application/pdf")
|
||||
|
||||
ENVELOPE_ID=$(echo $ENVELOPE_RESPONSE | jq -r '.id')
|
||||
echo "Created envelope: ${ENVELOPE_ID}"
|
||||
|
||||
# Step 2: Send the document for signing
|
||||
|
||||
echo "Sending document..."
|
||||
curl -s -X POST "${BASE_URL}/envelope/${ENVELOPE_ID}/distribute" \
|
||||
-H "Authorization: ${API_TOKEN}" \
|
||||
-H "Content-Type: application/json"
|
||||
|
||||
echo "Document sent for signing!"
|
||||
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Error Handling
|
||||
|
||||
The API returns standard HTTP status codes and JSON error responses:
|
||||
|
||||
| Status Code | Meaning |
|
||||
| ----------- | ------- |
|
||||
| `400` | Bad request - check your request payload |
|
||||
| `401` | Unauthorized - invalid or missing API token |
|
||||
| `404` | Not found - resource doesn't exist |
|
||||
| `429` | Rate limited - wait 60 seconds and retry |
|
||||
| `500` | Server error - retry or contact support |
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Description of what went wrong",
|
||||
"code": "ERROR_CODE",
|
||||
"statusCode": 400
|
||||
}
|
||||
````
|
||||
|
||||
### Common Errors
|
||||
|
||||
**Invalid file type:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Invalid file type. Only PDF files are supported.",
|
||||
"statusCode": 400
|
||||
}
|
||||
```
|
||||
|
||||
**Missing required field:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Recipient email is required",
|
||||
"statusCode": 400
|
||||
}
|
||||
```
|
||||
|
||||
**Envelope not found:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Envelope not found",
|
||||
"statusCode": 404
|
||||
}
|
||||
```
|
||||
|
||||
### Handling Rate Limits
|
||||
|
||||
The API allows 100 requests per minute per IP address. When rate limited, wait at least 60 seconds before retrying:
|
||||
|
||||
```javascript
|
||||
async function fetchWithRetry(url, options, maxRetries = 3) {
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (response.status === 429) {
|
||||
console.log('Rate limited, waiting 60 seconds...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 60000));
|
||||
continue;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new Error('Max retries exceeded');
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [API Reference](https://openapi.documenso.com/) - Full endpoint documentation with request/response schemas
|
||||
- [Webhooks](/docs/developers/webhooks/setup) - Get notified when documents are signed
|
||||
- [Templates](/docs/developers/api/templates) - Create reusable document templates
|
||||
- [SDKs](#official-sdks) - Use typed client libraries
|
||||
|
||||
### Official SDKs
|
||||
|
||||
For production applications, consider using the official SDKs:
|
||||
|
||||
- [TypeScript SDK](https://github.com/documenso/sdk-typescript)
|
||||
- [Python SDK](https://github.com/documenso/sdk-python)
|
||||
- [Go SDK](https://github.com/documenso/sdk-go)
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Getting Started
|
||||
description: Get your API key and make your first API call.
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Authentication"
|
||||
description="Generate an API key and authenticate your requests."
|
||||
href="/docs/developers/getting-started/authentication"
|
||||
/>
|
||||
<Card
|
||||
title="First API Call"
|
||||
description="Create your first document via the API."
|
||||
href="/docs/developers/getting-started/first-api-call"
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Getting Started",
|
||||
"pages": ["authentication", "first-api-call"]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Developer Guide
|
||||
description: Integrate Documenso into your applications using the REST API, webhooks, and embedding options.
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Authentication"
|
||||
description="Get your API key and authenticate requests."
|
||||
href="/docs/developers/getting-started/authentication"
|
||||
/>
|
||||
<Card
|
||||
title="First API Call"
|
||||
description="Create and send your first document via the API."
|
||||
href="/docs/developers/getting-started/first-api-call"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## Integration Options
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="API Reference"
|
||||
description="Documents, recipients, fields, templates, and teams."
|
||||
href="/docs/developers/api"
|
||||
/>
|
||||
<Card
|
||||
title="Webhooks"
|
||||
description="Receive notifications when documents are signed or updated."
|
||||
href="/docs/developers/webhooks"
|
||||
/>
|
||||
<Card
|
||||
title="Embedding"
|
||||
description="Embed signing experiences using direct links or React."
|
||||
href="/docs/developers/embedding"
|
||||
/>
|
||||
<Card
|
||||
title="Examples"
|
||||
description="Common integration patterns and workflows."
|
||||
href="/docs/developers/examples"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## API Base URL
|
||||
|
||||
```
|
||||
https://app.documenso.com/api/v2
|
||||
```
|
||||
|
||||
For self-hosted instances, replace with your instance URL:
|
||||
|
||||
```
|
||||
https://your-instance.com/api/v2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SDKs
|
||||
|
||||
Official SDKs are available for multiple languages:
|
||||
|
||||
- [TypeScript SDK](https://github.com/documenso/sdk-typescript)
|
||||
- [Python SDK](https://github.com/documenso/sdk-python)
|
||||
- [Go SDK](https://github.com/documenso/sdk-go)
|
||||
|
||||
---
|
||||
|
||||
## Looking for Something Else?
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="User Guide"
|
||||
description="Send documents using the web application."
|
||||
href="/docs/users"
|
||||
/>
|
||||
<Card
|
||||
title="Self-Hosting"
|
||||
description="Deploy your own Documenso instance."
|
||||
href="/docs/self-hosting"
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Run in Gitpod
|
||||
description: Get started with Documenso in a ready-to-use Gitpod workspace in your browser.
|
||||
---
|
||||
|
||||
Click below to launch a ready-to-use Gitpod workspace in your browser.
|
||||
|
||||
[](https://gitpod.io/#https://github.com/documenso/documenso)
|
||||
|
||||
## See Also
|
||||
|
||||
- [Developer Quickstart](/docs/developers/local-development/quickstart) - Local Docker-based setup
|
||||
- [Manual Setup](/docs/developers/local-development/manual) - Manual setup without Docker
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Local Development
|
||||
description: Learn how to set up Documenso for local development.
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
There are multiple ways of setting up Documenso for local development. At the moment of writing this documentation, there are 3 ways of running Documenso locally:
|
||||
|
||||
- [Using the developer quickstart with Docker](/docs/developers/local-development/quickstart)
|
||||
- [Manually setting up the development environment](/docs/developers/local-development/manual)
|
||||
- [Using Gitpod](/docs/developers/local-development/gitpod)
|
||||
|
||||
Pick the one that fits your needs the best.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- [Typescript](https://www.typescriptlang.org/) - Language
|
||||
- [React Router](https://reactrouter.com/) - Framework
|
||||
- [Prisma](https://www.prisma.io/) - ORM
|
||||
- [Tailwind](https://tailwindcss.com/) - CSS
|
||||
- [shadcn/ui](https://ui.shadcn.com/) - Component Library
|
||||
- [react-email](https://react.email/) - Email Templates
|
||||
- [tRPC](https://trpc.io/) - API
|
||||
- [@documenso/pdf-sign](https://www.npmjs.com/package/@documenso/pdf-sign) - PDF Signatures
|
||||
- [React-PDF](https://github.com/wojtekmaj/react-pdf) - Viewing PDFs
|
||||
- [PDF-Lib](https://github.com/Hopding/pdf-lib) - PDF manipulation
|
||||
- [Stripe](https://stripe.com/) - Payments
|
||||
|
||||
<div className="mt-16 flex items-center justify-center gap-4">
|
||||
<a href="https://documen.so/discord">
|
||||
<img
|
||||
src="https://img.shields.io/badge/Discord-documen.so/discord-%235865F2"
|
||||
alt="Join Documenso on Discord"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/documenso/documenso/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/documenso/documenso" alt="Github Stars" />
|
||||
</a>
|
||||
<a href="https://github.com/documenso/documenso/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License" />
|
||||
</a>
|
||||
<a href="https://github.com/documenso/documenso/pulse">
|
||||
<img
|
||||
src="https://img.shields.io/github/commit-activity/m/documenso/documenso"
|
||||
alt="Commits-per-month"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/documenso/documenso">
|
||||
<img
|
||||
alt="open in devcontainer"
|
||||
src="https://img.shields.io/static/v1?label=Dev%20Containers&message=Enabled&color=blue&logo=visualstudiocode"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/documenso/documenso/blob/main/CODE_OF_CONDUCT.md">
|
||||
<img
|
||||
src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg"
|
||||
alt="Contributor Covenant"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Contributing](/docs/developers/contributing) - Learn how to contribute to Documenso
|
||||
- [Self-Hosting](/docs/self-hosting) - Deploy your own instance
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: Manual Setup
|
||||
description: Manually set up Documenso on your machine for local development.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
# Manual Setup
|
||||
|
||||
Follow these steps to set up Documenso on your local machine:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Fork Documenso
|
||||
|
||||
Fork the [Documenso repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) to your GitHub account.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Clone repository
|
||||
|
||||
After forking the repository, clone it to your local device by using the following command:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/documenso
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Install dependencies
|
||||
|
||||
Run `npm i` in the root directory to install the dependencies required for the project.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Set up environment variables
|
||||
|
||||
Set up the following environment variables in the `.env` file:
|
||||
|
||||
```bash
|
||||
NEXTAUTH_SECRET
|
||||
NEXT_PUBLIC_WEBAPP_URL
|
||||
NEXT_PRIVATE_DATABASE_URL
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS
|
||||
```
|
||||
|
||||
Alternatively, you can run `cp .env.example .env` to get started with our handpicked defaults.
|
||||
|
||||
<Callout type="info">
|
||||
See the [Environment Variables](/docs/self-hosting/configuration/environment) page for more
|
||||
information.
|
||||
</Callout>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Create database schema
|
||||
|
||||
Create the database schema by running the following command:
|
||||
|
||||
```bash
|
||||
npm run prisma:migrate-dev
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Optional: seed the database
|
||||
|
||||
Seed the database with test data by running the following command:
|
||||
|
||||
```bash
|
||||
npm run prisma:seed -w @documenso/prisma
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Start the application
|
||||
|
||||
Run `npm run dev` in the root directory to start the application.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Access the application
|
||||
|
||||
Access the Documenso application by visiting `http://localhost:3000` in your web browser.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Callout type="info">
|
||||
Optional: Create your signing certificate. To generate your own using these steps and a Linux
|
||||
Terminal or Windows Subsystem for Linux (WSL), see **[Create your signing
|
||||
certificate](/docs/developers/local-development/signing-certificate)**.
|
||||
</Callout>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Developer Quickstart](/docs/developers/local-development/quickstart) - Quick Docker-based setup
|
||||
- [Signing Certificate](/docs/developers/local-development/signing-certificate) - Create a signing certificate
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Local Development",
|
||||
"pages": ["quickstart", "manual", "signing-certificate", "translations", "gitpod"]
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Developer Quickstart
|
||||
description: Quickly set up Documenso on your machine for local development with Docker and Docker Compose.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Quickstart
|
||||
|
||||
<Callout type="info">
|
||||
**Note**: This guide assumes that you have both [docker](https://docs.docker.com/get-docker/) and
|
||||
[docker-compose](https://docs.docker.com/compose/) installed on your machine.
|
||||
</Callout>
|
||||
|
||||
Want to get up and running quickly? Follow these steps:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Fork Documenso
|
||||
|
||||
Fork the [Documenso repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) to your GitHub account.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Clone repository
|
||||
|
||||
After forking the repository, clone it to your local device by using the following command:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-username>/documenso
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Set up environment variables
|
||||
|
||||
Set up your environment variables in the `.env` file using the `.env.example` file as a reference.
|
||||
|
||||
Alternatively, you can run `cp .env.example .env` to get started with our handpicked defaults.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Start database and mail server
|
||||
|
||||
Run `npm run dx` in the root directory.
|
||||
|
||||
This will spin up a Postgres database and inbucket mailserver in a docker container.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Start the application
|
||||
|
||||
Run `npm run dev` in the root directory to start the application.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### (Optional) Fasten the Process
|
||||
|
||||
Want it even faster? Just use:
|
||||
|
||||
```sh
|
||||
npm run d
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
### Access Points for the Project
|
||||
|
||||
You can access the following services:
|
||||
|
||||
- Main application - http://localhost:3000
|
||||
- Incoming Mail Access - http://localhost:9000
|
||||
- Database Connection Details:
|
||||
- Port: 54320
|
||||
- Connection: Use your favorite database client to connect to the database.
|
||||
- S3 Storage Dashboard - http://localhost:9001
|
||||
|
||||
## See Also
|
||||
|
||||
- [Manual Setup](/docs/developers/local-development/manual) - Set up without Docker
|
||||
- [Signing Certificate](/docs/developers/local-development/signing-certificate) - Create a certificate for local development
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Signing Certificate
|
||||
description: Learn how to create a free, self-signed certificate for local development.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
# Create Your Signing Certificate
|
||||
|
||||
Digitally signing documents requires a signing certificate in `.p12` format. You can either purchase one or create a free self-signed certificate.
|
||||
|
||||
Follow the steps below to create a free, self-signed certificate for local development.
|
||||
|
||||
<Callout type="warn">
|
||||
These steps should be run on a UNIX based system, otherwise you may run into an error.
|
||||
</Callout>
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Generate private key
|
||||
|
||||
Generate a private key using OpenSSL by running the following command:
|
||||
|
||||
```bash
|
||||
openssl genrsa -out private.key 2048
|
||||
```
|
||||
|
||||
This command generates a 2048-bit RSA key.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Generate self-signed certificate
|
||||
|
||||
Using the private key, generate a self-signed certificate by running the following command:
|
||||
|
||||
```bash
|
||||
openssl req -new -x509 -key private.key -out certificate.crt -days 365
|
||||
```
|
||||
|
||||
You will be prompted to enter some information, such as the certificate's Common Name (CN). Ensure that you provide the correct details. The `—days` parameter specifies the certificate's validity period.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Create `p12` certificate
|
||||
|
||||
Combine the private key and the self-signed certificate to create a `.p12` certificate. Use the following command:
|
||||
|
||||
```bash
|
||||
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt -legacy
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
When running the application in Docker, you may encounter permission issues when attempting to sign documents using your certificate (.p12) file. This happens because the application runs as a non-root user inside the container and needs read access to the certificate.
|
||||
|
||||
To resolve this, you'll need to update the certificate file permissions to allow the container user 1001, which runs NextJS, to read it:
|
||||
|
||||
```bash
|
||||
sudo chown 1001 certificate.p12
|
||||
```
|
||||
|
||||
</Callout>
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### `p12` certificate password
|
||||
|
||||
When you create the `.p12` certificate, you will be prompted to enter a password. Enter a strong password and keep it secure. Remember this password, as it will be required when using the certificate.
|
||||
|
||||
Note that for local development, the password can be left empty.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Add certificate to the project
|
||||
|
||||
Use the `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` environment variable to point at the certificate you created.
|
||||
|
||||
Details about environment variables associated with certificates can be found [here](/docs/self-hosting/configuration/signing-certificate).
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Signing Certificates (Self-Hosting)](/docs/self-hosting/configuration/signing-certificate) - Production certificate configuration
|
||||
- [Signing Certificates (Concepts)](/docs/concepts/signing-certificates) - How digital signing works
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Translations
|
||||
description: Handling translations in code.
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Documenso uses the following stack to handle translations:
|
||||
|
||||
- [Lingui](https://lingui.dev/) - React i10n library
|
||||
- [Crowdin](https://crowdin.com/) - Handles syncing translations
|
||||
- [OpenAI](https://openai.com/) - Provides AI translations
|
||||
|
||||
Additional reading can be found in the [Lingui documentation](https://lingui.dev/introduction).
|
||||
|
||||
## Quick Guide
|
||||
|
||||
If you require more in-depth information, please see the [Lingui documentation](https://lingui.dev/introduction).
|
||||
|
||||
### HTML
|
||||
|
||||
Wrap all text to translate in **`<Trans></Trans>`** tags exported from **@lingui/react/macro**.
|
||||
|
||||
```html
|
||||
<h1>
|
||||
<Trans>Title</Trans>
|
||||
</h1>
|
||||
```
|
||||
|
||||
For text that is broken into elements, but represent a whole sentence, you must wrap it in a Trans tag so ensure the full message is extracted correctly.
|
||||
|
||||
```html
|
||||
<h1>
|
||||
<Trans>
|
||||
This is one
|
||||
<span className="text-foreground/60">full</span>
|
||||
<a href="https://documenso.com">sentence</a>
|
||||
</Trans>
|
||||
</h1>
|
||||
```
|
||||
|
||||
### Constants outside of react components
|
||||
|
||||
```tsx
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
// Wrap text in msg`text to translate` when it's in a constant here, or another file/package.
|
||||
export const CONSTANT_WITH_MSG = {
|
||||
foo: msg`Hello`,
|
||||
bar: msg`World`,
|
||||
};
|
||||
|
||||
export const SomeComponent = () => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* This will render the correct translated text. */}
|
||||
<p>{_(CONSTANT_WITH_MSG.foo)}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Plurals
|
||||
|
||||
Lingui provides a Plural component to make it easy. See full documentation [here.](https://lingui.dev/ref/macro#plural-1)
|
||||
|
||||
```tsx
|
||||
// Basic usage.
|
||||
<Plural one="1 Recipient" other="# Recipients" value={recipients.length} />
|
||||
```
|
||||
|
||||
### Dates
|
||||
|
||||
Lingui provides a [DateTime instance](https://lingui.dev/ref/core#i18n.date) with the configured locale.
|
||||
|
||||
```tsx
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
export const SomeComponent = () => {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
return <Trans>The current date is {i18n.date(new Date(), { dateStyle: 'short' })}</Trans>;
|
||||
};
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Contributing Translations](/docs/developers/contributing/contributing-translations) - Help translate Documenso
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"title": "Developers",
|
||||
"description": "Integrate with Documenso",
|
||||
"root": true,
|
||||
"pages": [
|
||||
"getting-started",
|
||||
"api",
|
||||
"webhooks",
|
||||
"embedding",
|
||||
"examples",
|
||||
"local-development",
|
||||
"contributing",
|
||||
"demo-environment"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,679 @@
|
||||
---
|
||||
title: Webhook Events
|
||||
description: Reference for all webhook event types and payloads.
|
||||
---
|
||||
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Event Payload Structure
|
||||
|
||||
All webhook events share a common structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_COMPLETED",
|
||||
"payload": {
|
||||
// Document data with recipients
|
||||
},
|
||||
"createdAt": "2024-04-22T11:52:18.277Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### Top-Level Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ----------------- | -------- | ------------------------------------------------ |
|
||||
| `event` | string | Event type identifier (e.g., `DOCUMENT_CREATED`) |
|
||||
| `payload` | object | Document object with metadata and recipients |
|
||||
| `createdAt` | datetime | When the webhook event was created |
|
||||
| `webhookEndpoint` | string | The URL receiving this webhook |
|
||||
|
||||
### Payload Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ---------------- | --------- | ------------------------------------------------------ |
|
||||
| `id` | number | Document ID |
|
||||
| `externalId` | string? | External identifier for integration |
|
||||
| `userId` | number | Owner's user ID |
|
||||
| `authOptions` | object? | Document-level authentication options |
|
||||
| `formValues` | object? | PDF form values associated with the document |
|
||||
| `title` | string | Document title |
|
||||
| `status` | string | Current status: `DRAFT`, `PENDING`, `COMPLETED` |
|
||||
| `documentDataId` | string | Reference to the document's PDF data |
|
||||
| `visibility` | string | Document visibility setting |
|
||||
| `createdAt` | datetime | Document creation timestamp |
|
||||
| `updatedAt` | datetime | Last modification timestamp |
|
||||
| `completedAt` | datetime? | Completion timestamp (when all recipients have signed) |
|
||||
| `deletedAt` | datetime? | Deletion timestamp |
|
||||
| `teamId` | number? | Team ID if document belongs to a team |
|
||||
| `templateId` | number? | Template ID if created from a template |
|
||||
| `source` | string | Source: `DOCUMENT` or `TEMPLATE` |
|
||||
| `documentMeta` | object | Document metadata (subject, message, signing options) |
|
||||
| `Recipient` | array | List of recipient objects |
|
||||
|
||||
### Document Metadata Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ----------------------- | ------- | --------------------------------------- |
|
||||
| `id` | string | Metadata record identifier |
|
||||
| `subject` | string? | Email subject line |
|
||||
| `message` | string? | Email message body |
|
||||
| `timezone` | string | Timezone for date display |
|
||||
| `password` | string? | Document access password (if set) |
|
||||
| `dateFormat` | string | Date format string |
|
||||
| `redirectUrl` | string? | URL to redirect after signing |
|
||||
| `signingOrder` | string | `PARALLEL` or `SEQUENTIAL` |
|
||||
| `typedSignatureEnabled` | boolean | Whether typed signatures are allowed |
|
||||
| `language` | string | Document language code |
|
||||
| `distributionMethod` | string | How document is distributed |
|
||||
| `emailSettings` | object? | Custom email settings for this document |
|
||||
|
||||
### Recipient Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------------- | --------- | ------------------------------------------ |
|
||||
| `id` | number | Recipient ID |
|
||||
| `documentId` | number | Parent document ID |
|
||||
| `templateId` | number? | Template ID if created from a template |
|
||||
| `email` | string | Recipient email address |
|
||||
| `name` | string | Recipient name |
|
||||
| `token` | string | Unique signing token |
|
||||
| `documentDeletedAt` | datetime? | When the document was deleted (if deleted) |
|
||||
| `expired` | boolean? | Whether the recipient's link has expired |
|
||||
| `signedAt` | datetime? | When recipient signed |
|
||||
| `authOptions` | object? | Per-recipient authentication options |
|
||||
| `role` | string | Role: `SIGNER`, `VIEWER`, `APPROVER`, `CC` |
|
||||
| `signingOrder` | number? | Position in signing sequence |
|
||||
| `readStatus` | string | `NOT_OPENED` or `OPENED` |
|
||||
| `signingStatus` | string | `NOT_SIGNED`, `SIGNED`, or `REJECTED` |
|
||||
| `sendStatus` | string | `NOT_SENT` or `SENT` |
|
||||
| `rejectionReason` | string? | Reason if recipient rejected |
|
||||
|
||||
---
|
||||
|
||||
## Document Lifecycle Events
|
||||
|
||||
These events track the document through its lifecycle.
|
||||
|
||||
### `document.created`
|
||||
|
||||
Triggered when a new document is uploaded.
|
||||
|
||||
**Event name:** `DOCUMENT_CREATED`
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_CREATED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "DRAFT",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:44:43.341Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 52,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": null,
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "NOT_SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:44:44.779Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### `document.sent`
|
||||
|
||||
Triggered when a document is sent to recipients for signing.
|
||||
|
||||
**Event name:** `DOCUMENT_SENT`
|
||||
|
||||
The document status changes to `PENDING` and recipients have `sendStatus: "SENT"`.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_SENT",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:48:07.569Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 52,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": null,
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:48:07.945Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### `document.completed`
|
||||
|
||||
Triggered when all recipients have completed their required actions.
|
||||
|
||||
**Event name:** `DOCUMENT_COMPLETED`
|
||||
|
||||
The document status changes to `COMPLETED` and `completedAt` is set.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_COMPLETED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "COMPLETED",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:52:05.708Z",
|
||||
"completedAt": "2024-04-22T11:52:05.707Z",
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 50,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "reviewer@example.com",
|
||||
"name": "Jane Smith",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:51:10.055Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "VIEWER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "HkrptwS42ZBXdRKj1TyUo",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:52:05.688Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 2,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:52:18.277Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### `document.rejected`
|
||||
|
||||
Triggered when a recipient rejects the document.
|
||||
|
||||
**Event name:** `DOCUMENT_REJECTED`
|
||||
|
||||
The recipient's `signingStatus` changes to `REJECTED` and `rejectionReason` contains their reason.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_REJECTED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:48:07.569Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 52,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:48:07.569Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": "I do not agree with the terms",
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "REJECTED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:48:07.945Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### `document.cancelled`
|
||||
|
||||
Triggered when the document owner cancels a pending document.
|
||||
|
||||
**Event name:** `DOCUMENT_CANCELLED`
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_CANCELLED",
|
||||
"payload": {
|
||||
"id": 7,
|
||||
"externalId": null,
|
||||
"userId": 3,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "cm6exvn93006hi02ru90a265a",
|
||||
"createdAt": "2025-01-27T11:02:14.393Z",
|
||||
"updatedAt": "2025-01-27T11:03:16.387Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "cm6exvn96006ji02rqvzjvwoy",
|
||||
"subject": "",
|
||||
"message": "",
|
||||
"timezone": "Etc/UTC",
|
||||
"password": null,
|
||||
"dateFormat": "yyyy-MM-dd hh:mm a",
|
||||
"redirectUrl": "",
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 7,
|
||||
"documentId": 7,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "XkKx1HCs6Znm2UBJA2j6o",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": { "accessAuth": null, "actionAuth": null },
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2025-01-27T11:03:27.730Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recipient Events
|
||||
|
||||
Recipient events track individual signer actions. These events use the same payload structure as document events, but focus on a specific recipient's action.
|
||||
|
||||
### `document.opened`
|
||||
|
||||
Triggered when a recipient opens the document for the first time.
|
||||
|
||||
**Event name:** `DOCUMENT_OPENED`
|
||||
|
||||
The recipient's `readStatus` changes to `OPENED`.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_OPENED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:48:07.569Z",
|
||||
"completedAt": null,
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 52,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": null,
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:50:26.174Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
### `document.signed`
|
||||
|
||||
Triggered when a recipient signs the document.
|
||||
|
||||
**Event name:** `DOCUMENT_SIGNED`
|
||||
|
||||
The recipient's `signingStatus` changes to `SIGNED` and `signedAt` is populated.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_SIGNED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "contract.pdf",
|
||||
"status": "COMPLETED",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"createdAt": "2024-04-22T11:44:43.341Z",
|
||||
"updatedAt": "2024-04-22T11:52:05.708Z",
|
||||
"completedAt": "2024-04-22T11:52:05.707Z",
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"templateId": null,
|
||||
"source": "DOCUMENT",
|
||||
"documentMeta": {
|
||||
"id": "doc_meta_123",
|
||||
"subject": "Please sign this document",
|
||||
"message": "Hello, please review and sign this document.",
|
||||
"timezone": "UTC",
|
||||
"password": null,
|
||||
"dateFormat": "MM/DD/YYYY",
|
||||
"redirectUrl": null,
|
||||
"signingOrder": "PARALLEL",
|
||||
"typedSignatureEnabled": true,
|
||||
"language": "en",
|
||||
"distributionMethod": "EMAIL",
|
||||
"emailSettings": null
|
||||
},
|
||||
"Recipient": [
|
||||
{
|
||||
"id": 51,
|
||||
"documentId": 10,
|
||||
"templateId": null,
|
||||
"email": "signer@example.com",
|
||||
"name": "John Doe",
|
||||
"token": "HkrptwS42ZBXdRKj1TyUo",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:52:05.688Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
"sendStatus": "SENT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": "2024-04-22T11:52:18.577Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event Summary
|
||||
|
||||
| Event | Trigger | Key Changes |
|
||||
| -------------------- | ------------------------------- | ------------------------------------------------------------ |
|
||||
| `DOCUMENT_CREATED` | Document uploaded | `status: "DRAFT"` |
|
||||
| `DOCUMENT_SENT` | Document sent to recipients | `status: "PENDING"`, recipients `sendStatus: "SENT"` |
|
||||
| `DOCUMENT_OPENED` | Recipient opens document | Recipient `readStatus: "OPENED"` |
|
||||
| `DOCUMENT_SIGNED` | Recipient signs document | Recipient `signingStatus: "SIGNED"`, `signedAt` set |
|
||||
| `DOCUMENT_COMPLETED` | All recipients complete actions | `status: "COMPLETED"`, `completedAt` set |
|
||||
| `DOCUMENT_REJECTED` | Recipient rejects document | Recipient `signingStatus: "REJECTED"`, `rejectionReason` set |
|
||||
| `DOCUMENT_CANCELLED` | Owner cancels document | Document cancelled while pending |
|
||||
|
||||
---
|
||||
|
||||
## Handling Events
|
||||
|
||||
When processing webhook events:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
**Verify the signature** — Check the `X-Documenso-Secret` header matches your configured secret
|
||||
</Step>
|
||||
<Step>
|
||||
**Check event type** — Use the `event` field to determine the action
|
||||
</Step>
|
||||
<Step>
|
||||
**Process idempotently** — Webhooks may be retried, so handle duplicate events
|
||||
</Step>
|
||||
<Step>
|
||||
**Respond quickly** — Return a 200 status code within 30 seconds
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
```typescript
|
||||
app.post('/webhook', (req, res) => {
|
||||
const secret = req.headers['x-documenso-secret'];
|
||||
|
||||
if (secret !== process.env.WEBHOOK_SECRET) {
|
||||
return res.status(401).send('Unauthorized');
|
||||
}
|
||||
|
||||
const { event, payload } = req.body;
|
||||
|
||||
switch (event) {
|
||||
case 'DOCUMENT_COMPLETED':
|
||||
// Handle completed document
|
||||
console.log(`Document ${payload.id} completed`);
|
||||
break;
|
||||
case 'DOCUMENT_SIGNED':
|
||||
// Handle signature
|
||||
const signer = payload.Recipient.find((r) => r.signingStatus === 'SIGNED');
|
||||
console.log(`${signer?.name} signed document ${payload.id}`);
|
||||
break;
|
||||
case 'DOCUMENT_REJECTED':
|
||||
// Handle rejection
|
||||
const rejecter = payload.Recipient.find((r) => r.signingStatus === 'REJECTED');
|
||||
console.log(`${rejecter?.name} rejected: ${rejecter?.rejectionReason}`);
|
||||
break;
|
||||
}
|
||||
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Webhook Setup](/docs/developers/webhooks/setup) - Configure webhook endpoints
|
||||
- [Webhook Verification](/docs/developers/webhooks/verification) - Verify webhook signatures
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Webhooks
|
||||
description: Receive real-time notifications when documents are signed, completed, or updated.
|
||||
---
|
||||
|
||||
## How Webhooks Work
|
||||
|
||||
1. You configure a webhook URL in Documenso
|
||||
2. When an event occurs, Documenso sends an HTTP POST to your URL
|
||||
3. Your application processes the event and responds with 200 OK
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Setup"
|
||||
description="Configure webhook endpoints."
|
||||
href="/docs/developers/webhooks/setup"
|
||||
/>
|
||||
<Card
|
||||
title="Events"
|
||||
description="Available webhook event types."
|
||||
href="/docs/developers/webhooks/events"
|
||||
/>
|
||||
<Card
|
||||
title="Verification"
|
||||
description="Verify webhook signatures for security."
|
||||
href="/docs/developers/webhooks/verification"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
---
|
||||
|
||||
## Example Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "DOCUMENT_COMPLETED",
|
||||
"payload": {
|
||||
"id": 123,
|
||||
"title": "Contract",
|
||||
"status": "COMPLETED"
|
||||
},
|
||||
"createdAt": "2024-01-15T10:30:00.000Z",
|
||||
"webhookEndpoint": "https://your-endpoint.com/webhook"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Document Lifecycle](/docs/concepts/document-lifecycle) - Understanding document statuses
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Webhooks",
|
||||
"pages": ["setup", "events", "verification"]
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
---
|
||||
title: Webhook Setup
|
||||
description: Configure webhooks to receive real-time notifications about document events.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Overview
|
||||
|
||||
Webhooks are HTTP callbacks triggered by specific events in Documenso. When an event occurs (such as a document being signed), Documenso sends an HTTP POST request to your configured URL with details about the event.
|
||||
|
||||
Common use cases include:
|
||||
|
||||
- Syncing document status with your database
|
||||
- Triggering automated workflows when documents are signed
|
||||
- Integrating with CRM systems or other third-party services
|
||||
- Sending custom notifications to stakeholders
|
||||
|
||||
<Callout type="info">
|
||||
Webhooks are available for teams only. Personal accounts cannot configure webhooks.
|
||||
</Callout>
|
||||
|
||||
## Creating a Webhook Endpoint
|
||||
|
||||
Before configuring a webhook in Documenso, you need an endpoint that can receive HTTP POST requests. Here's a minimal example:
|
||||
|
||||
<Tabs items={['Node.js (Express)', 'Python (Flask)', 'Go']}>
|
||||
<Tab value="Node.js (Express)">
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.post('/webhooks/documenso', (req, res) => {
|
||||
const { event, payload, createdAt } = req.body;
|
||||
|
||||
console.log(`Received event: ${event}`);
|
||||
console.log(`Document ID: ${payload.id}`);
|
||||
console.log(`Document title: ${payload.title}`);
|
||||
|
||||
// Process the webhook event
|
||||
switch (event) {
|
||||
case 'DOCUMENT_COMPLETED':
|
||||
// Handle completed document
|
||||
break;
|
||||
case 'DOCUMENT_SIGNED':
|
||||
// Handle signed document
|
||||
break;
|
||||
// Handle other events...
|
||||
}
|
||||
|
||||
// Respond with 200 OK to acknowledge receipt
|
||||
res.status(200).json({ received: true });
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Webhook server running on port 3000');
|
||||
});
|
||||
|
||||
````
|
||||
</Tab>
|
||||
<Tab value="Python (Flask)">
|
||||
```python
|
||||
from flask import Flask, request, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/webhooks/documenso', methods=['POST'])
|
||||
def handle_webhook():
|
||||
data = request.get_json()
|
||||
|
||||
event = data.get('event')
|
||||
payload = data.get('payload')
|
||||
|
||||
print(f"Received event: {event}")
|
||||
print(f"Document ID: {payload.get('id')}")
|
||||
print(f"Document title: {payload.get('title')}")
|
||||
|
||||
# Process the webhook event
|
||||
if event == 'DOCUMENT_COMPLETED':
|
||||
# Handle completed document
|
||||
pass
|
||||
elif event == 'DOCUMENT_SIGNED':
|
||||
# Handle signed document
|
||||
pass
|
||||
|
||||
# Respond with 200 OK to acknowledge receipt
|
||||
return jsonify({'received': True}), 200
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(port=3000)
|
||||
````
|
||||
|
||||
</Tab>
|
||||
<Tab value="Go">
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebhookPayload struct {
|
||||
Event string `json:"event"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
func webhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var data WebhookPayload
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
http.Error(w, "Invalid payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Received event: %s\n", data.Event)
|
||||
fmt.Printf("Document ID: %v\n", data.Payload["id"])
|
||||
fmt.Printf("Document title: %v\n", data.Payload["title"])
|
||||
|
||||
// Process the webhook event
|
||||
switch data.Event {
|
||||
case "DOCUMENT_COMPLETED":
|
||||
// Handle completed document
|
||||
case "DOCUMENT_SIGNED":
|
||||
// Handle signed document
|
||||
}
|
||||
|
||||
// Respond with 200 OK to acknowledge receipt
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]bool{"received": true})
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/webhooks/documenso", webhookHandler)
|
||||
fmt.Println("Webhook server running on port 3000")
|
||||
http.ListenAndServe(":3000", nil)
|
||||
}
|
||||
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Callout type="warn">
|
||||
Always respond with a `200 OK` status within 30 seconds. Documenso will retry failed deliveries.
|
||||
</Callout>
|
||||
|
||||
## Configuring Webhooks in Documenso via the Dashboard
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Navigate to team settings
|
||||
|
||||
Click your avatar in the top right corner and select **Team settings** from the dropdown menu.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Open the webhooks tab
|
||||
|
||||
Navigate to the **Webhooks** tab in the team settings sidebar.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Create a new webhook
|
||||
|
||||
Click the **Create Webhook** button to open the configuration dialog.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Configure the webhook
|
||||
|
||||
Fill in the following fields:
|
||||
|
||||
| Field | Description |
|
||||
| ----- | ----------- |
|
||||
| **Webhook URL** | The HTTPS endpoint that will receive webhook events |
|
||||
| **Events** | Select which events should trigger this webhook |
|
||||
| **Secret** (optional) | A secret key used to sign the payload for verification |
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Save the webhook
|
||||
|
||||
Click **Create Webhook** to save your configuration. The webhook is now active and will receive events.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Webhook URL Requirements
|
||||
|
||||
Your webhook endpoint must meet these requirements:
|
||||
|
||||
| Requirement | Details |
|
||||
| ----------- | ------- |
|
||||
| **Protocol** | HTTPS required (HTTP not allowed in production) |
|
||||
| **Response** | Must return `2xx` status code within 30 seconds |
|
||||
| **Method** | Must accept HTTP POST requests |
|
||||
| **Content-Type** | Must accept `application/json` payloads |
|
||||
| **Availability** | Must be publicly accessible from the internet |
|
||||
|
||||
<Callout type="info">
|
||||
For local development, use a tunneling service like [ngrok](https://ngrok.com) or [localtunnel](https://localtunnel.me) to expose your local server.
|
||||
</Callout>
|
||||
|
||||
## Selecting Events
|
||||
|
||||
When creating a webhook, you can subscribe to one or more events:
|
||||
|
||||
| Event | Trigger |
|
||||
| ----- | ------- |
|
||||
| `DOCUMENT_CREATED` | A new document is created |
|
||||
| `DOCUMENT_SENT` | A document is sent to recipients |
|
||||
| `DOCUMENT_OPENED` | A recipient opens the document |
|
||||
| `DOCUMENT_SIGNED` | A recipient signs the document |
|
||||
| `DOCUMENT_COMPLETED` | All recipients have signed the document |
|
||||
| `DOCUMENT_REJECTED` | A recipient rejects the document |
|
||||
| `DOCUMENT_CANCELLED` | The document owner cancels the document |
|
||||
|
||||
You can subscribe to all events or select specific ones based on your needs. For example, if you only need to know when documents are fully signed, subscribe only to `DOCUMENT_COMPLETED`.
|
||||
|
||||
See [Webhook Events](/docs/developers/webhooks/events) for detailed payload information for each event type.
|
||||
|
||||
## Testing Webhooks
|
||||
|
||||
Documenso provides a built-in testing feature to verify your webhook endpoint works correctly.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Navigate to webhook details
|
||||
|
||||
Go to **Team Settings > Webhooks** and click on the webhook you want to test.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Click test
|
||||
|
||||
Click the **Test** button in the webhook details page.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Select an event type
|
||||
|
||||
Choose which event type you want to simulate from the dropdown.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Send test payload
|
||||
|
||||
Click **Send** to dispatch a test webhook with sample data to your endpoint.
|
||||
|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
The test payload contains realistic sample data so you can verify your endpoint processes events correctly. After sending, you can view the response in the webhook call logs.
|
||||
|
||||
### Viewing Webhook Logs
|
||||
|
||||
Each webhook subscription maintains a log of all delivery attempts. To view logs:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Go to **Team Settings > Webhooks**
|
||||
</Step>
|
||||
<Step>
|
||||
Click on a webhook to view its details
|
||||
</Step>
|
||||
<Step>
|
||||
Review the logs
|
||||
|
||||
Each webhook call shows the following details:
|
||||
- Status (success/failure)
|
||||
- Event type
|
||||
- Timestamp
|
||||
- Response code
|
||||
- Request and response bodies
|
||||
|
||||
Click any call to see full details including headers and response data.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Resending Failed Webhooks
|
||||
|
||||
If a webhook delivery fails, you can manually resend it:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Navigate to the webhook call details page
|
||||
</Step>
|
||||
<Step>
|
||||
Click the **Resend** button
|
||||
</Step>
|
||||
<Step>
|
||||
Documenso will attempt to deliver the same payload again
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Retry Policy
|
||||
|
||||
When a webhook delivery fails (non-2xx response or timeout), Documenso automatically retries with exponential backoff:
|
||||
|
||||
| Attempt | Delay |
|
||||
| ------- | ----- |
|
||||
| 1 | Immediate |
|
||||
| 2 | 1 minute |
|
||||
| 3 | 5 minutes |
|
||||
| 4 | 30 minutes |
|
||||
| 5 | 2 hours |
|
||||
|
||||
After 5 failed attempts, the webhook is marked as failed and no further automatic retries occur. You can manually resend failed webhooks from the dashboard.
|
||||
|
||||
<Callout type="warn">
|
||||
If your endpoint consistently fails, consider reviewing your server logs and ensuring your endpoint meets all [URL requirements](#webhook-url-requirements).
|
||||
</Callout>
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Use HTTPS">
|
||||
Always use HTTPS endpoints in production.
|
||||
</Accordion>
|
||||
<Accordion title="Verify signatures">
|
||||
Use a webhook secret and verify the `X-Documenso-Secret` header (see [Verification](/docs/developers/webhooks/verification)).
|
||||
</Accordion>
|
||||
<Accordion title="Validate payloads">
|
||||
Validate incoming data before processing.
|
||||
</Accordion>
|
||||
<Accordion title="Respond quickly">
|
||||
Return 200 OK immediately, then process asynchronously.
|
||||
</Accordion>
|
||||
<Accordion title="Idempotency">
|
||||
Handle duplicate deliveries gracefully.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Webhook Events](/docs/developers/webhooks/events) - Detailed payload structure for each event type
|
||||
- [Webhook Verification](/docs/developers/webhooks/verification) - Secure your webhooks with signature verification
|
||||
```
|
||||
@@ -0,0 +1,290 @@
|
||||
---
|
||||
title: Webhook Verification
|
||||
description: Verify webhook signatures for security.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Overview
|
||||
|
||||
Verifying webhook requests ensures that incoming payloads originate from Documenso and have not been tampered with. Without verification, attackers could forge requests to your endpoint and trigger unintended actions.
|
||||
|
||||
## How Documenso Signs Webhooks
|
||||
|
||||
When you configure a webhook with a secret, Documenso includes that secret in every webhook request via the `X-Documenso-Secret` header. Your server should compare this header value against your stored secret to authenticate the request.
|
||||
|
||||
```bash
|
||||
POST /your-webhook-endpoint HTTP/1.1
|
||||
Host: your-server.com
|
||||
Content-Type: application/json
|
||||
X-Documenso-Secret: your_webhook_secret_here
|
||||
|
||||
{"event": "DOCUMENT_COMPLETED", "payload": {...}}
|
||||
```
|
||||
|
||||
## Signature Header Format
|
||||
|
||||
| Header | Description |
|
||||
| -------------------- | ------------------------------------------------------- |
|
||||
| `X-Documenso-Secret` | The secret key you configured when creating the webhook |
|
||||
|
||||
The header contains your webhook secret as a plain string. If you did not configure a secret, the header will be an empty string.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Extract the `X-Documenso-Secret` header from the incoming request
|
||||
</Step>
|
||||
<Step>
|
||||
Compare it against your stored webhook secret using a constant-time comparison
|
||||
</Step>
|
||||
<Step>
|
||||
Reject the request if the values do not match
|
||||
</Step>
|
||||
<Step>
|
||||
Process the webhook payload if verification succeeds
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warn">
|
||||
Always use constant-time string comparison to prevent timing attacks. Standard equality operators
|
||||
(`===` or `==`) can leak information about the secret through response time variations.
|
||||
</Callout>
|
||||
|
||||
## Code Examples
|
||||
|
||||
<Tabs items={['Node.js (Express)', 'Python (Flask)']}>
|
||||
<Tab value="Node.js (Express)">
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const WEBHOOK_SECRET = process.env.DOCUMENSO_WEBHOOK_SECRET;
|
||||
|
||||
function verifyWebhookSignature(receivedSecret, expectedSecret) {
|
||||
if (!expectedSecret) {
|
||||
// No secret configured, skip verification
|
||||
// Not recommended for production
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!receivedSecret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use constant-time comparison to prevent timing attacks
|
||||
try {
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(receivedSecret),
|
||||
Buffer.from(expectedSecret),
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
app.post('/webhooks/documenso', (req, res) => {
|
||||
const receivedSecret = req.headers['x-documenso-secret'];
|
||||
|
||||
if (!verifyWebhookSignature(receivedSecret, WEBHOOK_SECRET)) {
|
||||
console.error('Webhook verification failed');
|
||||
return res.status(401).json({ error: 'Invalid signature' });
|
||||
}
|
||||
|
||||
// Signature verified, process the webhook
|
||||
const { event, payload } = req.body;
|
||||
console.log(`Verified webhook: ${event}`);
|
||||
|
||||
// Process the event...
|
||||
|
||||
res.status(200).json({ received: true });
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Webhook server running on port 3000');
|
||||
});
|
||||
|
||||
````
|
||||
</Tab>
|
||||
<Tab value="Python (Flask)">
|
||||
```python
|
||||
import hmac
|
||||
import os
|
||||
from flask import Flask, request, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
WEBHOOK_SECRET = os.environ.get('DOCUMENSO_WEBHOOK_SECRET')
|
||||
|
||||
def verify_webhook_signature(received_secret, expected_secret):
|
||||
"""Verify the webhook signature using constant-time comparison."""
|
||||
if not expected_secret:
|
||||
# No secret configured, skip verification
|
||||
# Not recommended for production
|
||||
return True
|
||||
|
||||
if not received_secret:
|
||||
return False
|
||||
|
||||
# Use constant-time comparison to prevent timing attacks
|
||||
return hmac.compare_digest(received_secret, expected_secret)
|
||||
|
||||
@app.route('/webhooks/documenso', methods=['POST'])
|
||||
def handle_webhook():
|
||||
received_secret = request.headers.get('X-Documenso-Secret', '')
|
||||
|
||||
if not verify_webhook_signature(received_secret, WEBHOOK_SECRET):
|
||||
print('Webhook verification failed')
|
||||
return jsonify({'error': 'Invalid signature'}), 401
|
||||
|
||||
# Signature verified, process the webhook
|
||||
data = request.get_json()
|
||||
event = data.get('event')
|
||||
payload = data.get('payload')
|
||||
|
||||
print(f'Verified webhook: {event}')
|
||||
|
||||
# Process the event...
|
||||
|
||||
return jsonify({'received': True}), 200
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(port=3000)
|
||||
````
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Handling Verification Failures
|
||||
|
||||
When verification fails, follow these practices:
|
||||
|
||||
| Action | Description |
|
||||
| ------------------- | ------------------------------------------------------------------ |
|
||||
| Return 401 status | Respond with `401 Unauthorized` to indicate authentication failure |
|
||||
| Log the attempt | Record failed attempts for security monitoring |
|
||||
| Do not process | Never process the payload if verification fails |
|
||||
| Do not leak details | Avoid exposing information about why verification failed |
|
||||
|
||||
```javascript
|
||||
app.post('/webhooks/documenso', (req, res) => {
|
||||
const receivedSecret = req.headers['x-documenso-secret'];
|
||||
|
||||
if (!verifyWebhookSignature(receivedSecret, WEBHOOK_SECRET)) {
|
||||
// Log for monitoring but don't expose details
|
||||
console.error('Webhook verification failed', {
|
||||
timestamp: new Date().toISOString(),
|
||||
ip: req.ip,
|
||||
});
|
||||
|
||||
// Generic error response
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
// Continue processing...
|
||||
});
|
||||
```
|
||||
|
||||
### Common Verification Issues
|
||||
|
||||
| Issue | Cause | Solution |
|
||||
| --------------- | --------------------------------------- | ------------------------------------------------------------------ |
|
||||
| Secret mismatch | Webhook secret changed or misconfigured | Verify the secret in your environment matches the one in Documenso |
|
||||
| Empty header | Webhook created without a secret | Add a secret to the webhook configuration in Documenso |
|
||||
| Encoding issues | String encoding mismatch | Ensure both secrets use the same encoding (UTF-8) |
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Use strong secrets">
|
||||
Generate a cryptographically secure random string for your webhook secret:
|
||||
|
||||
```bash
|
||||
# Generate a 32-byte random secret
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Store secrets securely">
|
||||
Never hardcode secrets in your source code. Use environment variables or a secrets manager:
|
||||
|
||||
```javascript
|
||||
// Good: Environment variable
|
||||
const WEBHOOK_SECRET = process.env.DOCUMENSO_WEBHOOK_SECRET;
|
||||
|
||||
// Bad: Hardcoded
|
||||
const WEBHOOK_SECRET = 'my-secret-key'; // Never do this
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Rotate secrets periodically">
|
||||
Update your webhook secret periodically:
|
||||
|
||||
1. Generate a new secret
|
||||
2. Update your server to accept both old and new secrets temporarily
|
||||
3. Update the webhook configuration in Documenso
|
||||
4. Remove the old secret from your server
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Validate payload structure">
|
||||
After verifying the signature, validate the payload structure before processing:
|
||||
|
||||
```javascript
|
||||
const { event, payload, createdAt } = req.body;
|
||||
|
||||
if (!event || !payload) {
|
||||
return res.status(400).json({ error: 'Invalid payload structure' });
|
||||
}
|
||||
|
||||
// Validate event is a known type
|
||||
const validEvents = [
|
||||
'DOCUMENT_CREATED',
|
||||
'DOCUMENT_SENT',
|
||||
'DOCUMENT_OPENED',
|
||||
'DOCUMENT_SIGNED',
|
||||
'DOCUMENT_COMPLETED',
|
||||
'DOCUMENT_REJECTED',
|
||||
'DOCUMENT_CANCELLED',
|
||||
];
|
||||
|
||||
if (!validEvents.includes(event)) {
|
||||
console.warn(`Unknown event type: ${event}`);
|
||||
}
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Use HTTPS">
|
||||
Always use HTTPS endpoints in production to encrypt data in transit, including the secret header.
|
||||
</Accordion>
|
||||
<Accordion title="Implement rate limiting">
|
||||
Protect your endpoint from abuse with rate limiting:
|
||||
|
||||
```javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
|
||||
const webhookLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 100, // 100 requests per minute
|
||||
message: { error: 'Too many requests' },
|
||||
});
|
||||
|
||||
app.post('/webhooks/documenso', webhookLimiter, (req, res) => {
|
||||
// Handle webhook...
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Webhook Setup](/docs/developers/webhooks/setup) - Configure webhook endpoints and secrets
|
||||
- [Webhook Events](/docs/developers/webhooks/events) - Event types and payload structure
|
||||
@@ -0,0 +1,163 @@
|
||||
---
|
||||
title: Documenso Documentation
|
||||
description: Learn how to send documents for signing, integrate with the API, or self-host your own instance.
|
||||
full: true
|
||||
---
|
||||
|
||||
import {
|
||||
BookOpenIcon,
|
||||
CodeIcon,
|
||||
FileTextIcon,
|
||||
ServerIcon,
|
||||
ShieldCheckIcon,
|
||||
UserIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
<div className="not-prose max-w-4xl mx-auto px-4">
|
||||
<div className="mb-12 text-center pt-6">
|
||||
<p className="text-lg text-fd-muted-foreground max-w-2xl mx-auto">
|
||||
The open-source document signing platform. Send documents for signatures, integrate with your apps, or self-host with full control.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-3 mb-12">
|
||||
<a href="/docs/users" className="group relative flex flex-col rounded-xl border bg-fd-card p-6 transition-all hover:border-fd-primary/50 hover:shadow-md">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-lg bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
|
||||
<UserIcon className="size-6" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold mb-2">User Guide</h2>
|
||||
<p className="text-sm text-fd-muted-foreground mb-4 flex-1">
|
||||
Send documents, create templates, and manage your team using the web application.
|
||||
</p>
|
||||
<span className="text-sm font-medium text-fd-primary">Get started →</span>
|
||||
</a>
|
||||
|
||||
<a href="/docs/developers" className="group relative flex flex-col rounded-xl border bg-fd-card p-6 transition-all hover:border-fd-primary/50 hover:shadow-md">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-lg bg-blue-500/10 text-blue-600 dark:text-blue-400">
|
||||
<CodeIcon className="size-6" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold mb-2">Developer Guide</h2>
|
||||
<p className="text-sm text-fd-muted-foreground mb-4 flex-1">
|
||||
Integrate document signing into your applications with the REST API, webhooks, and embedding.
|
||||
</p>
|
||||
<span className="text-sm font-medium text-fd-primary">View API docs →</span>
|
||||
</a>
|
||||
|
||||
<a href="/docs/self-hosting" className="group relative flex flex-col rounded-xl border bg-fd-card p-6 transition-all hover:border-fd-primary/50 hover:shadow-md">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-lg bg-purple-500/10 text-purple-600 dark:text-purple-400">
|
||||
<ServerIcon className="size-6" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold mb-2">Self-Hosting Guide</h2>
|
||||
<p className="text-sm text-fd-muted-foreground mb-4 flex-1">
|
||||
Deploy your own Documenso instance with Docker, Kubernetes, or Railway.
|
||||
</p>
|
||||
<span className="text-sm font-medium text-fd-primary">Deploy now →</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2 mb-12">
|
||||
<div className="rounded-xl border bg-fd-card/50 p-6">
|
||||
<h3 className="font-semibold mb-4 flex items-center gap-2">
|
||||
<BookOpenIcon className="size-5 text-fd-muted-foreground" />
|
||||
Quick Start
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-2">Send your first document</h4>
|
||||
<ol className="text-sm text-fd-muted-foreground space-y-1 list-decimal list-inside">
|
||||
<li><a href="/docs/users/getting-started/create-account" className="text-fd-primary hover:underline">Create an account</a></li>
|
||||
<li><a href="/docs/users/getting-started/send-first-document" className="text-fd-primary hover:underline">Upload and send a document</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-2">Integrate with the API</h4>
|
||||
<ol className="text-sm text-fd-muted-foreground space-y-1 list-decimal list-inside">
|
||||
<li><a href="/docs/developers/getting-started/authentication" className="text-fd-primary hover:underline">Get your API key</a></li>
|
||||
<li><a href="/docs/developers/getting-started/first-api-call" className="text-fd-primary hover:underline">Make your first API call</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-2">Deploy self-hosted</h4>
|
||||
<ol className="text-sm text-fd-muted-foreground space-y-1 list-decimal list-inside">
|
||||
<li><a href="/docs/self-hosting/getting-started/requirements" className="text-fd-primary hover:underline">Check requirements</a></li>
|
||||
<li><a href="/docs/self-hosting/getting-started/quick-start" className="text-fd-primary hover:underline">Run with Docker</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border bg-fd-card/50 p-6">
|
||||
<h3 className="font-semibold mb-4 flex items-center gap-2">
|
||||
<BookOpenIcon className="size-5 text-fd-muted-foreground" />
|
||||
Core Concepts
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<a href="/docs/concepts/document-lifecycle" className="rounded-lg border bg-fd-background p-3 text-sm hover:border-fd-primary/50 transition-colors">
|
||||
<div className="font-medium mb-1">Document Lifecycle</div>
|
||||
<div className="text-xs text-fd-muted-foreground">Draft to completed</div>
|
||||
</a>
|
||||
<a href="/docs/concepts/recipient-roles" className="rounded-lg border bg-fd-background p-3 text-sm hover:border-fd-primary/50 transition-colors">
|
||||
<div className="font-medium mb-1">Recipient Roles</div>
|
||||
<div className="text-xs text-fd-muted-foreground">Signers and approvers</div>
|
||||
</a>
|
||||
<a href="/docs/concepts/field-types" className="rounded-lg border bg-fd-background p-3 text-sm hover:border-fd-primary/50 transition-colors">
|
||||
<div className="font-medium mb-1">Field Types</div>
|
||||
<div className="text-xs text-fd-muted-foreground">Signatures and inputs</div>
|
||||
</a>
|
||||
<a href="/docs/concepts/signing-certificates" className="rounded-lg border bg-fd-background p-3 text-sm hover:border-fd-primary/50 transition-colors">
|
||||
<div className="font-medium mb-1">Signing Certificates</div>
|
||||
<div className="text-xs text-fd-muted-foreground">Digital verification</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 mb-12">
|
||||
<a href="/docs/compliance" className="flex items-start gap-4 rounded-xl border bg-fd-card/50 p-5 transition-all hover:border-fd-primary/50">
|
||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-amber-500/10 text-amber-600 dark:text-amber-400">
|
||||
<ShieldCheckIcon className="size-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-1">Compliance & Legal</h3>
|
||||
<p className="text-sm text-fd-muted-foreground">
|
||||
ESIGN, UETA, eIDAS compliance, GDPR, and signature levels explained.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="/docs/policies" className="flex items-start gap-4 rounded-xl border bg-fd-card/50 p-5 transition-all hover:border-fd-primary/50">
|
||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-slate-500/10 text-slate-600 dark:text-slate-400">
|
||||
<FileTextIcon className="size-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-1">Policies & Licensing</h3>
|
||||
<p className="text-sm text-fd-muted-foreground">
|
||||
AGPL and Enterprise licenses, fair use, privacy policy, and support.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border bg-gradient-to-r from-fd-primary/5 to-fd-primary/10 p-6 text-center">
|
||||
<h3 className="font-semibold mb-2">Join the Community</h3>
|
||||
<p className="text-sm text-fd-muted-foreground mb-4">
|
||||
Documenso is open source. Contribute, ask questions, or share feedback.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
<a href="https://github.com/documenso/documenso" className="inline-flex items-center gap-2 rounded-lg bg-fd-background border px-4 py-2 text-sm font-medium hover:bg-fd-accent transition-colors">
|
||||
<svg className="size-4" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a href="https://documen.so/discord" className="inline-flex items-center gap-2 rounded-lg bg-fd-background border px-4 py-2 text-sm font-medium hover:bg-fd-accent transition-colors">
|
||||
<svg className="size-4" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"></path></svg>
|
||||
Discord
|
||||
</a>
|
||||
<a href="https://documenso.com" className="inline-flex items-center gap-2 rounded-lg bg-fd-primary text-fd-primary-foreground px-4 py-2 text-sm font-medium hover:bg-fd-primary/90 transition-colors">
|
||||
Try Documenso
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Documentation",
|
||||
"pages": [
|
||||
"---Guides---",
|
||||
"users",
|
||||
"developers",
|
||||
"self-hosting",
|
||||
"---Resources---",
|
||||
"concepts",
|
||||
"compliance",
|
||||
"policies"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
---
|
||||
title: Community Edition
|
||||
description: The open-source version of Documenso, available under the AGPL-3.0 license. Includes core document signing functionality and can be self-hosted.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## AGPL-3.0 License Overview
|
||||
|
||||
The GNU Affero General Public License version 3 (AGPL-3.0) is a copyleft license designed to ensure software freedom. It is similar to GPL-3.0 but includes an additional "network clause" that addresses software-as-a-service use cases.
|
||||
|
||||
Key characteristics:
|
||||
|
||||
- **Copyleft**: Derivative works must use the same license
|
||||
- **Network clause**: Network access triggers the same obligations as distribution
|
||||
- **Source availability**: Users must be able to access the source code
|
||||
|
||||
The full license text is available in the [LICENSE file](https://github.com/documenso/documenso/blob/main/LICENSE) in the repository.
|
||||
|
||||
## What You Can Do
|
||||
|
||||
Under the AGPL-3.0 license, you are permitted to:
|
||||
|
||||
| Action | Description |
|
||||
| ------------------ | ------------------------------------------------------------- |
|
||||
| **Use** | Run Documenso for any purpose, including commercial use |
|
||||
| **Study** | Access and examine the complete source code |
|
||||
| **Modify** | Make changes and customizations to the software |
|
||||
| **Distribute** | Share copies of the original or modified software |
|
||||
| **Self-host** | Deploy on your own servers, cloud infrastructure, or locally |
|
||||
| **Offer services** | Provide document signing services to users (with obligations) |
|
||||
|
||||
## What You Must Do
|
||||
|
||||
When using or distributing the Community Edition, you must comply with these requirements:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Source Code Disclosure">
|
||||
If you modify Documenso and make it available over a network, you must:
|
||||
|
||||
- Provide access to the complete source code of your modified version
|
||||
- Make the source available through the same network interface, or provide a written offer to supply it
|
||||
- Include all scripts and instructions needed to build and install the software
|
||||
|
||||
<Callout type="warn">
|
||||
The network clause is the distinguishing feature of AGPL-3.0. If users interact with your modified
|
||||
Documenso instance over a network (whether internal or external), you must provide them access to
|
||||
the source code.
|
||||
</Callout>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Attribution">
|
||||
You must maintain proper attribution:
|
||||
|
||||
- Keep all copyright notices intact in the source code
|
||||
- Include a copy of the AGPL-3.0 license with any distribution
|
||||
- Clearly state any modifications you have made
|
||||
- Preserve author attributions and license headers
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="License Continuity">
|
||||
Derivative works must be licensed under AGPL-3.0:
|
||||
|
||||
- Forks must use the same license
|
||||
- Modifications cannot be made proprietary
|
||||
- Combined works that incorporate Documenso code are subject to AGPL-3.0
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Enterprise Edition Boundary">
|
||||
Features located in the `packages/ee/` directory of the codebase are not licensed under AGPL-3.0 and require an active [Enterprise license](/docs/policies/enterprise-edition) to use. If you are self-hosting the Community Edition, do not enable or use features from the `packages/ee/` folder without an enterprise license.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Features Included
|
||||
|
||||
The Community Edition includes the core Documenso functionality:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Document Management">
|
||||
- Upload and prepare PDF documents for signing
|
||||
- Add multiple recipients with different roles (signer, approver, viewer, etc.)
|
||||
- Configure signing order and workflow
|
||||
- Track document status and completion
|
||||
</Accordion>
|
||||
<Accordion title="Field Types">
|
||||
- Signature fields (draw, type, or upload)
|
||||
- Initial fields
|
||||
- Text fields
|
||||
- Date fields
|
||||
- Checkbox fields
|
||||
- Number fields
|
||||
- Dropdown/select fields
|
||||
- Radio button fields
|
||||
</Accordion>
|
||||
<Accordion title="Signing Experience">
|
||||
- Email-based signing workflow
|
||||
- Direct signing links
|
||||
- Signing certificate integration
|
||||
- Completed document download
|
||||
</Accordion>
|
||||
<Accordion title="Templates">
|
||||
- Create reusable document templates
|
||||
- Pre-configure recipients and fields
|
||||
- Generate documents from templates via UI or API
|
||||
</Accordion>
|
||||
<Accordion title="API Access">
|
||||
- REST API for document operations
|
||||
- Webhook notifications for events
|
||||
- API token authentication
|
||||
</Accordion>
|
||||
<Accordion title="Self-Hosting">
|
||||
- Docker and Docker Compose deployment
|
||||
- Kubernetes deployment option
|
||||
- Railway one-click deployment
|
||||
- PostgreSQL database support
|
||||
- S3-compatible storage support
|
||||
- SMTP email configuration
|
||||
|
||||
For the complete list of configuration options, see the [Self-Hosting Guide](/docs/self-hosting).
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Support Options
|
||||
|
||||
Community Edition users have access to community-based support:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Discord">
|
||||
The Documenso Discord server is the primary community support channel. You can:
|
||||
|
||||
- Ask questions and get help from other users
|
||||
- Share your use cases and solutions
|
||||
- Report issues and discuss features
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="GitHub">
|
||||
- **[GitHub Discussions](https://github.com/documenso/documenso/discussions)**: For questions, ideas, and general discussion
|
||||
- **[GitHub Issues](https://github.com/documenso/documenso/issues)**: For confirmed bug reports with reproducible steps
|
||||
</Accordion>
|
||||
<Accordion title="Documentation">
|
||||
- [Self-Hosting Guide](/docs/self-hosting) for deployment and configuration
|
||||
- [Developer Guide](/docs/developers) for API integration
|
||||
- [Troubleshooting Guide](/docs/self-hosting/maintenance/troubleshooting) for common issues
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
<Callout type="info">
|
||||
Community support is provided on a best-effort basis by volunteers and Documenso team members. For
|
||||
guaranteed response times and dedicated support, consider the Enterprise Edition.
|
||||
</Callout>
|
||||
|
||||
## Contributing Back
|
||||
|
||||
Contributions to Documenso are welcome and appreciated. You can contribute in several ways:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Code Contributions">
|
||||
1. Fork the [Documenso repository](https://github.com/documenso/documenso)
|
||||
2. Create a branch for your changes
|
||||
3. Submit a pull request with a clear description
|
||||
4. Respond to code review feedback
|
||||
|
||||
All contributions are licensed under AGPL-3.0.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Other Contributions">
|
||||
- Report bugs with detailed reproduction steps
|
||||
- Suggest features through GitHub Discussions
|
||||
- Improve documentation
|
||||
- Help other users in Discord or GitHub Discussions
|
||||
- Translate the interface to other languages
|
||||
</Accordion>
|
||||
<Accordion title="Contributor Guidelines">
|
||||
Before contributing, review:
|
||||
|
||||
- The [CONTRIBUTING.md](https://github.com/documenso/documenso/blob/main/CONTRIBUTING.md) file in the repository
|
||||
- Existing issues and discussions to avoid duplicates
|
||||
- The coding standards used in the project
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## When to Consider Enterprise Edition
|
||||
|
||||
The Community Edition is appropriate for many use cases. However, you may need the Enterprise Edition if you:
|
||||
|
||||
- Cannot comply with AGPL-3.0 source disclosure requirements
|
||||
- Need to keep modifications proprietary
|
||||
- Require dedicated support with guaranteed response times
|
||||
- Need enterprise-specific features (SSO, advanced audit logs, etc.)
|
||||
- Have corporate policies that prohibit AGPL-licensed software
|
||||
|
||||
See [Enterprise Edition](/docs/policies/enterprise-edition) for details on commercial licensing.
|
||||
|
||||
## Related
|
||||
|
||||
- [Licenses](/docs/policies/licenses) - Overview of both licensing options
|
||||
- [Licenses](/docs/policies/licenses) - Detailed AGPL-3.0 explanation
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - Commercial license details
|
||||
- [Self-Hosting Guide](/docs/self-hosting) - Deploy your own instance
|
||||
- [Support](/docs/policies/support) - All support options
|
||||
- [Full AGPL-3.0 License Text](https://www.gnu.org/licenses/agpl-3.0.html) - Complete license terms
|
||||
- [GitHub Repository](https://github.com/documenso/documenso) - Source code and LICENSE file
|
||||
@@ -0,0 +1,244 @@
|
||||
---
|
||||
title: Enterprise Edition
|
||||
description: Commercial license that removes AGPL-3.0 source code disclosure requirements, allowing organisations to integrate Documenso into proprietary products.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## When You Need Enterprise
|
||||
|
||||
The Enterprise Edition is required when you:
|
||||
|
||||
- **Build proprietary products**: Integrate Documenso into commercial software without releasing source code
|
||||
- **Offer SaaS with modifications**: Provide document signing as part of your platform with custom modifications you want to keep private
|
||||
- **Cannot comply with AGPL-3.0**: Corporate policies or legal requirements prohibit use of AGPL-licensed software
|
||||
- **Need to keep modifications private**: Any changes to Documenso source code that you don't want to disclose
|
||||
|
||||
<Callout type="info">
|
||||
If you run Documenso without modifications, or your modifications are open source under AGPL-3.0,
|
||||
the Community Edition is sufficient.
|
||||
</Callout>
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
| Scenario | License Needed |
|
||||
| -------------------------------------------- | ------------------ |
|
||||
| Internal use without modifications | Community Edition |
|
||||
| Internal use with private modifications | Enterprise Edition |
|
||||
| SaaS product using Documenso API only | Community Edition |
|
||||
| SaaS product with modified Documenso code | Enterprise Edition |
|
||||
| White-label product with proprietary changes | Enterprise Edition |
|
||||
| Open-source project under AGPL-3.0 | Community Edition |
|
||||
|
||||
## Features and Benefits
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="License Rights">
|
||||
The Enterprise license grants:
|
||||
|
||||
- **No source disclosure**: Keep your modifications private. No obligation to share code with users or the public.
|
||||
- **Proprietary integration**: Embed Documenso in closed-source products without licensing conflicts.
|
||||
- **Sublicensing options**: Distribute Documenso as part of your product to your customers (terms vary by agreement).
|
||||
- **Perpetual use**: License terms that allow continued use of the licensed version.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Enterprise-Only Features">
|
||||
Enterprise licenses include access to features not available in the Community Edition:
|
||||
|
||||
- Stripe Billing Module
|
||||
- Organisation Authentication Portal (SSO with SAML and OIDC)
|
||||
- Document Action Reauthentication (Passkeys and 2FA)
|
||||
- 21 CFR Part 11 Compliance
|
||||
- Email Domains (custom sender addresses)
|
||||
- Embed Authoring
|
||||
- Embed Authoring White Label
|
||||
- Custom signing certificates
|
||||
- Priority feature requests
|
||||
|
||||
<Callout type="info">
|
||||
The canonical list of enterprise-gated features is maintained in the `packages/ee/FEATURES` file
|
||||
in the codebase. Features in the `packages/ee/` directory require an active Enterprise license.
|
||||
</Callout>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="License Setup">
|
||||
1. Acquire a license by contacting [sales](https://documen.so/enterprise)
|
||||
2. Access your license key at [license.documenso.com](https://license.documenso.com)
|
||||
3. Set the environment variable:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY="your-license-key-here"
|
||||
```
|
||||
|
||||
4. Restart your Documenso instance
|
||||
5. Verify the license is active in the **Admin Panel** under the **Stats** section
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Licensing Terms
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Grant of License">
|
||||
The Enterprise license is a commercial agreement between your organisation and Documenso. Key terms include:
|
||||
|
||||
- **Scope**: License to use, modify, and distribute Documenso as specified in your agreement
|
||||
- **Exclusions**: The license does not grant rights to Documenso trademarks beyond what's specified
|
||||
- **Termination**: License remains valid as long as subscription is active and terms are followed
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="What the License Covers">
|
||||
- Documenso application code
|
||||
- Self-hosted deployments
|
||||
- Modifications and derivative works
|
||||
- Internal and external distribution (per agreement terms)
|
||||
</Accordion>
|
||||
<Accordion title="What the License Does Not Cover">
|
||||
- Third-party dependencies (these retain their original licenses)
|
||||
- Documenso Cloud service (separate subscription)
|
||||
- Professional services (available separately)
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Pricing Model
|
||||
|
||||
Enterprise pricing is based on:
|
||||
|
||||
| Factor | Description |
|
||||
| ------------------- | ----------------------------------- |
|
||||
| **Deployment type** | Self-hosted or Documenso Cloud |
|
||||
| **User count** | Number of users sending documents |
|
||||
| **Volume** | Monthly document signing volume |
|
||||
| **Support level** | Standard or premium support options |
|
||||
| **Contract term** | Annual or multi-year agreements |
|
||||
|
||||
### Pricing Tiers
|
||||
|
||||
Contact [sales](https://documen.so/enterprise) for current pricing. Enterprise agreements typically include:
|
||||
|
||||
- Annual subscription billing
|
||||
- Volume discounts for larger deployments
|
||||
- Multi-year discount options
|
||||
- Custom terms for specific requirements
|
||||
|
||||
<Callout type="warn">
|
||||
Pricing and terms are subject to change. Contact sales for current rates and availability.
|
||||
</Callout>
|
||||
|
||||
## Support Included
|
||||
|
||||
Enterprise licenses include dedicated support beyond community channels.
|
||||
|
||||
<Tabs items={['Standard Enterprise Support', 'Premium Support Options']}>
|
||||
<Tab value="Standard Enterprise Support">
|
||||
|
||||
- Email support with priority response
|
||||
- Target response time within 4-8 business hours
|
||||
- Deployment and configuration assistance
|
||||
- Access to technical account resources
|
||||
|
||||
</Tab>
|
||||
<Tab value="Premium Support Options">
|
||||
|
||||
Available as add-ons or with higher-tier plans:
|
||||
|
||||
- Dedicated support contact
|
||||
- Scheduled calls for complex issues
|
||||
- Implementation guidance
|
||||
- Custom SLA agreements
|
||||
- On-call support for critical issues
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
See [Support](/docs/policies/support) for complete support options.
|
||||
|
||||
## How to Get Started
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Evaluate your requirements
|
||||
|
||||
Determine if you need the Enterprise Edition:
|
||||
|
||||
- Review the [license comparison](/docs/policies/licenses) to understand AGPL-3.0 vs commercial license
|
||||
- Identify your use case from the scenarios above
|
||||
- List any enterprise features you require
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Contact sales
|
||||
|
||||
Reach out to discuss your needs:
|
||||
|
||||
- Email: [sales@documenso.com](https://documen.so/enterprise)
|
||||
- Include: Organisation name, use case, estimated user count, deployment preference (self-hosted or cloud)
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Evaluation
|
||||
|
||||
Options for evaluating Documenso before purchase:
|
||||
|
||||
- Test with the Community Edition (same codebase, AGPL-3.0 license)
|
||||
- Request an evaluation license for Enterprise features
|
||||
- Schedule a demo with the sales team
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Agreement and deployment
|
||||
|
||||
Once terms are agreed:
|
||||
|
||||
1. Sign the Enterprise license agreement
|
||||
2. Receive license key and access credentials
|
||||
3. Deploy using [self-hosting guides](/docs/self-hosting) or access Documenso Cloud
|
||||
4. Configure Enterprise features with support assistance
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Ongoing
|
||||
|
||||
- Renew subscription annually (or per agreement terms)
|
||||
- Access support channels for assistance
|
||||
- Receive updates and new features per your agreement
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Can I switch from Community to Enterprise?">
|
||||
Yes. Your existing deployment can transition to the Enterprise license. Contact
|
||||
[sales](https://documen.so/enterprise) to discuss migration.
|
||||
</Accordion>
|
||||
<Accordion title="Does the Enterprise license include Documenso Cloud?">
|
||||
The license and cloud service are separate. Enterprise cloud plans include both the commercial
|
||||
license and hosted service. Self-hosted Enterprise licenses cover only the license rights.
|
||||
</Accordion>
|
||||
<Accordion title="What happens if I don't renew?">
|
||||
If you don't renew, you lose access to support and updates. Depending on your agreement terms,
|
||||
you may continue using the last licensed version or need to transition to the Community Edition
|
||||
(with AGPL-3.0 compliance).
|
||||
</Accordion>
|
||||
<Accordion title="Can I use Enterprise features with the Community Edition?">
|
||||
No. Enterprise-only features require an active Enterprise license. The Community Edition
|
||||
includes core document signing functionality.
|
||||
</Accordion>
|
||||
<Accordion title="Is there a trial period?">
|
||||
Contact [sales](https://documen.so/enterprise) to discuss evaluation options for your
|
||||
organisation.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Related
|
||||
|
||||
- [Community Edition](/docs/policies/community-edition) - AGPL-3.0 open-source license
|
||||
- [Licenses](/docs/policies/licenses) - Complete licensing overview and FAQ
|
||||
- [Support](/docs/policies/support) - Support channels and response times
|
||||
- [Self-Hosting](/docs/self-hosting) - Deployment guides for self-hosted installations
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Fair Use Policy
|
||||
description: Fair use guidelines for Documenso Cloud to keep things sustainable for everyone.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
We like to overdeliver, but we cannot overcommit.
|
||||
|
||||
Our plans are designed to be generous and flexible without forcing customers into rigid volume limits they may never use. Estimating usage at scale is hard, especially over short periods. This fair use policy exists to keep plans sustainable while allowing us to add more value wherever possible without overformalising restrictions.
|
||||
|
||||
We offer our plans without limits on signing or API volume because we want users and customers to make the most of their accounts. If you are on one of these plans, we ask you to abide by this fair use policy.
|
||||
|
||||
## Spirit of the Plan
|
||||
|
||||
Use the limitless plans as much as you like. They are meant to offer a lot. Please respect the spirit and intended scope of the account.
|
||||
|
||||
**What happens if I go beyond the scope of this policy?** We will ask you to upgrade to a fitting plan or custom pricing. We will not block your account without reaching out. You can message us with any questions.
|
||||
|
||||
### Do
|
||||
|
||||
- Sign as many documents as you need with the individual plan for your single business or organisation
|
||||
- Use the API and automation tools to automate your signing workflows
|
||||
- Experiment with plans and integrations while testing what you want to build
|
||||
|
||||
### Don't
|
||||
|
||||
- Use an individual account API to power a platform or product
|
||||
- Run a large company signing thousands of documents per day on a small team plan
|
||||
- Expect enterprise-level support on a fair support plan
|
||||
- Overthink this policy — if you are a paying customer, we want you to win
|
||||
|
||||
## Rate Limits
|
||||
|
||||
API and application requests are rate-limited to protect service availability. Limits apply per user or API token.
|
||||
|
||||
When a limit is reached, requests return a `429 Too Many Requests` response with a `Retry-After` header indicating when to retry.
|
||||
|
||||
| Action | Limit | Window |
|
||||
| --- | --- | --- |
|
||||
| API requests (v1 and v2) | 100 requests | 1 minute |
|
||||
| File uploads | 20 requests | 1 minute |
|
||||
| AI features | 3 requests | 1 minute |
|
||||
|
||||
Authentication endpoints (login, signup, password reset, etc.) are also rate-limited to protect against abuse.
|
||||
|
||||
<Callout type="info">
|
||||
Rate limits may vary by plan. Enterprise plans can include higher or custom limits. Contact
|
||||
[sales](https://documen.so/sales) for details.
|
||||
</Callout>
|
||||
|
||||
<Callout type="info">
|
||||
Self-hosted deployments can configure their own rate limits. You control all limits based on your
|
||||
infrastructure capacity. See [Self-Hosting](/docs/self-hosting) for deployment options.
|
||||
</Callout>
|
||||
|
||||
## Fair Support
|
||||
|
||||
Fair support includes reasonable application-level help for self-hosted users. We will help you get unstuck and point you in the right direction when issues come up. Support is provided in good faith and within reasonable time and effort limits.
|
||||
|
||||
We are not your operations team and cannot take responsibility for running, monitoring, or maintaining your infrastructure.
|
||||
|
||||
If you are unsure whether something falls within fair use or fair support, reach out. We are happy to talk it through.
|
||||
|
||||
## Related
|
||||
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - Commercial licensing and custom limits
|
||||
- [Self-Hosting](/docs/self-hosting) - Deploy without cloud-imposed limits
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Policies
|
||||
description: License, privacy, terms, and support information
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="License"
|
||||
description="AGPL-3.0 license terms and what they mean for you."
|
||||
href="/docs/policies/licenses"
|
||||
/>
|
||||
<Card
|
||||
title="Privacy Policy"
|
||||
description="How Documenso collects, uses, and protects your data."
|
||||
href="/docs/policies/privacy"
|
||||
/>
|
||||
<Card
|
||||
title="Terms of Service"
|
||||
description="Terms governing your use of Documenso."
|
||||
href="/docs/policies/terms"
|
||||
/>
|
||||
<Card
|
||||
title="Security"
|
||||
description="Security practices and vulnerability disclosure."
|
||||
href="/docs/policies/security"
|
||||
/>
|
||||
<Card
|
||||
title="Support"
|
||||
description="Getting help and available support options."
|
||||
href="/docs/policies/support"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## Full Legal Documents
|
||||
|
||||
The complete legal documents are available on the main Documenso website:
|
||||
|
||||
- [Privacy Policy](https://documenso.com/privacy)
|
||||
- [Terms of Service](https://documenso.com/terms)
|
||||
|
||||
The pages in this documentation provide summaries and practical guidance. For binding legal terms, refer to the full documents linked above.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [Compliance](/docs/compliance) - Electronic signature standards and regulations
|
||||
@@ -0,0 +1,173 @@
|
||||
---
|
||||
title: Licenses
|
||||
description: Documenso uses a dual licensing model with an open-source AGPL-3.0 license (Community Edition) and a commercial license (Enterprise Edition).
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Dual Licensing Overview
|
||||
|
||||
| Aspect | Community Edition | Enterprise Edition |
|
||||
| ----------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| **License** | AGPL-3.0 | Commercial |
|
||||
| **Cost** | Free | Paid |
|
||||
| **Source code disclosure** | Required for network use | Not required |
|
||||
| **Proprietary modifications** | Not permitted | Permitted |
|
||||
| **Support** | Community | Dedicated |
|
||||
| **Use case** | Open-source projects, internal tools with AGPL compliance | Commercial products, SaaS offerings, proprietary integrations |
|
||||
|
||||
<Tabs items={['Community Edition', 'Enterprise Edition']}>
|
||||
<Tab value="Community Edition">
|
||||
|
||||
## Community Edition (AGPL-3.0)
|
||||
|
||||
The Community Edition is licensed under the GNU Affero General Public License version 3 (AGPL-3.0). This is a copyleft license that preserves software freedom.
|
||||
|
||||
### What AGPL-3.0 Allows
|
||||
|
||||
- Use Documenso for any purpose, including commercial use
|
||||
- Access, study, and modify the source code
|
||||
- Distribute copies of the original or modified software
|
||||
- Self-host Documenso on your own infrastructure
|
||||
|
||||
### What AGPL-3.0 Requires
|
||||
|
||||
The AGPL-3.0 has specific requirements that apply when you modify or distribute Documenso:
|
||||
|
||||
**Source Code Disclosure**: If you modify Documenso and make it available over a network, you must provide access to the complete source code of your modified version under AGPL-3.0.
|
||||
|
||||
**Attribution**: You must keep all copyright notices intact and include a copy of the AGPL-3.0 license with any distribution.
|
||||
|
||||
**Same License**: Derivative works must be licensed under AGPL-3.0.
|
||||
|
||||
<Callout type="info">
|
||||
The "network clause" is the distinguishing feature of AGPL-3.0. Unlike GPL-3.0, AGPL-3.0 treats
|
||||
network access as distribution. If users interact with your modified Documenso instance over a
|
||||
network, you must provide them access to the source code.
|
||||
</Callout>
|
||||
|
||||
### When to Use Community Edition
|
||||
|
||||
The Community Edition is appropriate when you:
|
||||
|
||||
- Are building an open-source project
|
||||
- Can comply with AGPL-3.0 requirements
|
||||
- Use Documenso internally without modifications
|
||||
- Want to contribute to the open-source ecosystem
|
||||
- Do not need to keep modifications proprietary
|
||||
|
||||
See [Community Edition](/docs/policies/community-edition) for complete details.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Enterprise Edition">
|
||||
|
||||
## Enterprise Edition (Commercial License)
|
||||
|
||||
The Enterprise Edition provides a commercial license that removes the AGPL-3.0 requirements. This allows organisations to use, modify, and integrate Documenso without source code disclosure obligations.
|
||||
|
||||
### What the Enterprise License Provides
|
||||
|
||||
**No Source Code Disclosure**: Keep your modifications private. You are not required to share changes with users or the public.
|
||||
|
||||
**Proprietary Integration**: Integrate Documenso into proprietary products and services without licensing conflicts.
|
||||
|
||||
**Commercial Support**: Access to dedicated support channels and SLAs.
|
||||
|
||||
**Additional Features**: Enterprise-specific features not available in the Community Edition.
|
||||
|
||||
### When to Use Enterprise Edition
|
||||
|
||||
The Enterprise Edition is appropriate when you:
|
||||
|
||||
- Build commercial products that incorporate Documenso
|
||||
- Offer document signing as part of a SaaS platform
|
||||
- Cannot or prefer not to open-source your modifications
|
||||
- Need to comply with corporate policies that prohibit AGPL software
|
||||
- Require dedicated support and SLAs
|
||||
|
||||
<Callout type="warn">
|
||||
If you modify Documenso and provide network access to users (internal or external) without
|
||||
complying with AGPL-3.0 source disclosure requirements, you need an Enterprise license.
|
||||
</Callout>
|
||||
|
||||
See [Enterprise Edition](/docs/policies/enterprise-edition) for complete details and pricing.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Choosing the Right License
|
||||
|
||||
Use this decision tree to determine which license fits your needs:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Do you modify Documenso?">
|
||||
**No modifications**: Both licenses work. Community Edition is free and sufficient if you run Documenso as-is.
|
||||
|
||||
**Yes, with modifications**: Continue to the next question.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Are your modifications available as open source?">
|
||||
**Yes, under AGPL-3.0**: Community Edition is appropriate. Your modifications must be available to users who access your instance.
|
||||
|
||||
**No, modifications are proprietary**: Enterprise Edition is required.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Are you embedding Documenso in a commercial product?">
|
||||
**Open-source product under AGPL-3.0**: Community Edition is appropriate.
|
||||
|
||||
**Proprietary product**: Enterprise Edition is required.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Internal use only?">
|
||||
Using Documenso internally without providing network access to external users does not trigger AGPL-3.0 distribution requirements. However, if your organisation has policies against AGPL software or you make modifications you want to keep private, consider the Enterprise Edition.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## License FAQ
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Can I use the Community Edition for commercial purposes?">
|
||||
Yes. AGPL-3.0 permits commercial use. You must comply with the license terms, including source
|
||||
code disclosure if you modify and distribute or provide network access.
|
||||
</Accordion>
|
||||
<Accordion title="Does using the Documenso API require a commercial license?">
|
||||
No. Calling the Documenso API from your application does not create a derivative work. Your
|
||||
application remains under your chosen license. However, if you modify Documenso itself and
|
||||
provide network access, AGPL-3.0 requirements apply.
|
||||
</Accordion>
|
||||
<Accordion title='What counts as a "modification" under AGPL-3.0?'>
|
||||
Modifying the source code, forking the repository and making changes, or linking Documenso
|
||||
libraries into your application in a way that creates a combined work. Configuration changes and
|
||||
using Documenso through its API are not modifications.
|
||||
</Accordion>
|
||||
<Accordion title="Can I white-label Documenso?">
|
||||
You can change branding for your deployment under both licenses. Under AGPL-3.0, you must retain
|
||||
copyright notices and license information in the source code. Under the Enterprise license, you
|
||||
have more flexibility with branding requirements.
|
||||
</Accordion>
|
||||
<Accordion title="Do signed documents fall under the license?">
|
||||
No. The license applies to the software, not content created with the software. Documents you
|
||||
sign are your property and are not subject to AGPL-3.0.
|
||||
</Accordion>
|
||||
<Accordion title="What happens if I switch from Community to Enterprise Edition?">
|
||||
Your existing deployments can transition to the Enterprise license. Contact
|
||||
[sales](https://documen.so/enterprise) to discuss migration.
|
||||
</Accordion>
|
||||
<Accordion title="Can I contribute to Documenso under the Enterprise license?">
|
||||
Contributions to the main Documenso repository are licensed under AGPL-3.0. The Enterprise
|
||||
license applies only to your private use and modifications.
|
||||
</Accordion>
|
||||
<Accordion title="Is there a trial for the Enterprise Edition?">
|
||||
Contact [sales](https://documen.so/enterprise) to discuss evaluation options.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Related
|
||||
|
||||
- [Licenses](/docs/policies/licenses) - Detailed AGPL-3.0 license explanation
|
||||
- [Community Edition](/docs/policies/community-edition) - Open-source edition details
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - Commercial license details
|
||||
- [Full AGPL-3.0 License Text](https://www.gnu.org/licenses/agpl-3.0.html) - Complete license terms
|
||||
- [GitHub Repository](https://github.com/documenso/documenso) - Source code and LICENSE file
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Policies",
|
||||
"pages": [
|
||||
"licenses",
|
||||
"community-edition",
|
||||
"enterprise-edition",
|
||||
"fair-use",
|
||||
"privacy",
|
||||
"terms",
|
||||
"security",
|
||||
"support"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Privacy Policy
|
||||
description: How Documenso collects, uses, and protects your data.
|
||||
---
|
||||
|
||||
Our full Privacy Policy is available at [documenso.com/privacy](https://documenso.com/privacy).
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
title: Security
|
||||
description: Security practices for Documenso Cloud and self-hosted deployments, along with our vulnerability disclosure process.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Security Practices
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Development Security">
|
||||
- **Code review**: All code changes require review before merging - **Dependency management**:
|
||||
Dependencies are regularly updated and monitored for vulnerabilities - **Static analysis**:
|
||||
Automated security scanning is part of the CI/CD pipeline - **Open source**: The codebase is
|
||||
publicly available for security review at
|
||||
[github.com/documenso/documenso](https://github.com/documenso/documenso)
|
||||
</Accordion>
|
||||
<Accordion title="Access Controls">
|
||||
- **Role-based access**: Teams support member, manager, and admin roles with distinct
|
||||
permissions - **API token scoping**: API tokens can be scoped to specific teams and set with
|
||||
expiration dates - **Session management**: Users can view and revoke active sessions - **Audit
|
||||
logging**: Document actions are logged with timestamps and IP addresses
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Infrastructure Security
|
||||
|
||||
### Documenso Cloud
|
||||
|
||||
The hosted cloud service uses the following security measures:
|
||||
|
||||
| Layer | Implementation |
|
||||
| -------------- | ------------------------------------------------------ |
|
||||
| **Hosting** | Infrastructure hosted in EU data centers |
|
||||
| **Network** | TLS 1.2+ for all connections |
|
||||
| **Database** | Managed PostgreSQL with automated backups |
|
||||
| **Storage** | Encrypted object storage for documents |
|
||||
| **Monitoring** | 24/7 infrastructure monitoring and alerting |
|
||||
| **Updates** | Regular security patches applied to all infrastructure |
|
||||
|
||||
### Self-Hosted
|
||||
|
||||
Self-hosted deployments are responsible for their own infrastructure security. See [Self-Hosted Security Considerations](#self-hosted-security-considerations) below.
|
||||
|
||||
## Data Encryption
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Encryption in Transit">
|
||||
All data transmitted to and from Documenso is encrypted using TLS 1.2 or higher. This includes:
|
||||
|
||||
- Web application traffic
|
||||
- API requests
|
||||
- Email delivery (when supported by the receiving server)
|
||||
- Webhook payloads
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Encryption at Rest">
|
||||
For Documenso Cloud:
|
||||
|
||||
- Database contents are encrypted at rest
|
||||
- Document storage uses encrypted object storage
|
||||
- Backups are encrypted
|
||||
|
||||
For self-hosted deployments, encryption at rest depends on your infrastructure configuration.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Application-Level Encryption">
|
||||
Documenso encrypts sensitive data at the application level:
|
||||
|
||||
- Encryption keys are configured via `NEXT_PRIVATE_ENCRYPTION_KEY` and `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY`
|
||||
- Session tokens are signed using `NEXTAUTH_SECRET`
|
||||
- Passwords are hashed using bcrypt
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Authentication Security
|
||||
|
||||
### Supported Authentication Methods
|
||||
|
||||
| Method | Description |
|
||||
| ----------------------------- | ------------------------------------------------ |
|
||||
| **Email and password** | Traditional authentication with hashed passwords |
|
||||
| **OAuth providers** | Google and Microsoft authentication |
|
||||
| **Generic OIDC** | Any OpenID Connect provider |
|
||||
| **Team SSO** | SAML-based single sign-on for enterprise teams |
|
||||
| **Two-factor authentication** | TOTP-based 2FA with recovery codes |
|
||||
| **Passkeys** | WebAuthn-based passwordless authentication |
|
||||
|
||||
### Password Requirements
|
||||
|
||||
- Minimum length enforced
|
||||
- Passwords are hashed using bcrypt before storage
|
||||
- Password reset tokens are time-limited and single-use
|
||||
|
||||
### Session Security
|
||||
|
||||
- Sessions can be viewed and revoked from account settings
|
||||
- Session tokens are rotated on authentication events
|
||||
- Idle sessions expire after a configurable period
|
||||
|
||||
## Vulnerability Disclosure
|
||||
|
||||
Documenso operates a responsible disclosure process for security vulnerabilities.
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Reporting a Vulnerability">
|
||||
If you discover a security vulnerability, please report it by emailing:
|
||||
|
||||
**security@documenso.com**
|
||||
|
||||
Include the following information:
|
||||
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Any suggested fixes (optional)
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Response Timeline">
|
||||
| Stage | Timeline |
|
||||
| --------------------- | ------------------- |
|
||||
| **Acknowledgment** | Within 48 hours |
|
||||
| **Initial triage** | Within 5 days |
|
||||
| **Status update** | Within 10 days |
|
||||
| **Resolution target** | Depends on severity |
|
||||
</Accordion>
|
||||
<Accordion title="Scope">
|
||||
<Tabs items={['In scope', 'Out of scope']}>
|
||||
<Tab value="In scope">
|
||||
|
||||
The following are in scope for vulnerability reports:
|
||||
|
||||
- Documenso application code
|
||||
- Authentication and authorization flaws
|
||||
- Data exposure vulnerabilities
|
||||
- Injection vulnerabilities
|
||||
- Cross-site scripting (XSS)
|
||||
- Cross-site request forgery (CSRF)
|
||||
|
||||
</Tab>
|
||||
<Tab value="Out of scope">
|
||||
|
||||
The following are out of scope:
|
||||
|
||||
- Social engineering attacks
|
||||
- Denial of service attacks
|
||||
- Issues in third-party dependencies (report to the upstream project)
|
||||
- Issues requiring physical access to a user's device
|
||||
- Theoretical vulnerabilities without proof of concept
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Recognition">
|
||||
We acknowledge security researchers who responsibly disclose vulnerabilities. With your permission, we will credit you when the fix is released.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
<Callout type="warn">
|
||||
Do not publicly disclose vulnerabilities until they have been addressed. Public disclosure of
|
||||
unpatched vulnerabilities puts users at risk.
|
||||
</Callout>
|
||||
|
||||
## Security Updates
|
||||
|
||||
### Notification
|
||||
|
||||
Security updates are announced through:
|
||||
|
||||
- [GitHub releases](https://github.com/documenso/documenso/releases)
|
||||
- [GitHub security advisories](https://github.com/documenso/documenso/security/advisories)
|
||||
|
||||
### Update Policy
|
||||
|
||||
- Critical vulnerabilities are patched as quickly as possible
|
||||
- Security patches are backported to supported versions when feasible
|
||||
- Release notes include security-related changes
|
||||
|
||||
### Staying Updated
|
||||
|
||||
For self-hosted deployments:
|
||||
|
||||
1. Watch the [GitHub repository](https://github.com/documenso/documenso) for releases
|
||||
2. Subscribe to security advisories
|
||||
3. Apply updates promptly, especially security patches
|
||||
|
||||
See [Upgrades](/docs/self-hosting/maintenance/upgrades) for update procedures.
|
||||
|
||||
## Self-Hosted Security Considerations
|
||||
|
||||
When self-hosting Documenso, you are responsible for the security of your deployment. The following recommendations apply:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Infrastructure">
|
||||
- **Use HTTPS**: Configure TLS certificates for all traffic - **Firewall rules**: Restrict
|
||||
access to necessary ports only - **Network isolation**: Place the database on a private network
|
||||
- **Regular updates**: Keep the host OS and dependencies updated
|
||||
</Accordion>
|
||||
<Accordion title="Configuration">
|
||||
- **Strong secrets**: Generate cryptographically random values for: - `NEXTAUTH_SECRET` -
|
||||
`NEXT_PRIVATE_ENCRYPTION_KEY` - `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY` - **Database security**:
|
||||
Use strong passwords and restrict database access - **Environment variables**: Store secrets
|
||||
securely (do not commit to version control)
|
||||
</Accordion>
|
||||
<Accordion title="Operations">
|
||||
- **Backups**: Implement regular encrypted backups - **Monitoring**: Set up logging and alerting
|
||||
for security events - **Access control**: Limit who has access to the server and database -
|
||||
**Incident response**: Have a plan for responding to security incidents
|
||||
</Accordion>
|
||||
<Accordion title="Signing Certificates">
|
||||
- Store signing certificates securely - Use hardware security modules (HSM) for high-security
|
||||
requirements - Rotate certificates according to your security policy
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
See [Environment Variables](/docs/self-hosting/configuration/environment) for security-related configuration options.
|
||||
|
||||
<Callout type="info">
|
||||
Self-hosted deployments have full control over security but also full responsibility. Consider
|
||||
your organisation's security requirements and compliance obligations when configuring your
|
||||
deployment.
|
||||
</Callout>
|
||||
|
||||
## Contact
|
||||
|
||||
For security-related inquiries:
|
||||
|
||||
- **Security vulnerabilities**: security@documenso.com
|
||||
- **General questions**: support@documenso.com
|
||||
- **GitHub**: [github.com/documenso/documenso](https://github.com/documenso/documenso)
|
||||
|
||||
## Related
|
||||
|
||||
- [Security Settings](/docs/users/settings/security) - Account security configuration
|
||||
- [Privacy Policy](/docs/policies/privacy) - Data handling practices
|
||||
- [GDPR](/docs/compliance/gdpr) - Data protection compliance
|
||||
- [Self-Hosting](/docs/self-hosting) - Deploy on your own infrastructure
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Configuration reference
|
||||
@@ -0,0 +1,240 @@
|
||||
---
|
||||
title: Support
|
||||
description: Support channels and options available depending on your needs and subscription level.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Support Channels
|
||||
|
||||
| Channel | Availability | Best For |
|
||||
| --------------------------- | ------------ | --------------------------------------- |
|
||||
| Community (Discord, GitHub) | All users | General questions, community discussion |
|
||||
| Email Support | Paid plans | Account issues, billing questions |
|
||||
| Priority Support | Enterprise | Urgent issues, dedicated assistance |
|
||||
|
||||
## Community Support
|
||||
|
||||
Community support is available to all users, including those on free plans and self-hosted deployments.
|
||||
|
||||
<Tabs items={['Discord', 'GitHub']}>
|
||||
<Tab value="Discord">
|
||||
|
||||
Join the Documenso Discord server to:
|
||||
|
||||
- Ask questions and get help from the community
|
||||
- Share feedback and feature requests
|
||||
- Connect with other Documenso users
|
||||
- Stay updated on announcements
|
||||
|
||||
Discord is best for general questions and discussions. Response times vary based on community availability.
|
||||
|
||||
</Tab>
|
||||
<Tab value="GitHub">
|
||||
|
||||
Use GitHub for technical discussions and bug reports:
|
||||
|
||||
- **[GitHub Discussions](https://github.com/documenso/documenso/discussions)**: Ask questions, share ideas, and discuss features
|
||||
- **[GitHub Issues](https://github.com/documenso/documenso/issues)**: Report confirmed bugs with reproducible steps
|
||||
|
||||
When opening an issue, include:
|
||||
|
||||
1. Documenso version
|
||||
2. Deployment method (Cloud, self-hosted Docker, Kubernetes, etc.)
|
||||
3. Steps to reproduce the problem
|
||||
4. Expected vs actual behavior
|
||||
5. Relevant logs (with sensitive information redacted)
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Callout type="warn">
|
||||
Do not report security vulnerabilities through public GitHub issues. See the [Security
|
||||
Issues](#security-issues) section below.
|
||||
</Callout>
|
||||
|
||||
## Paid Support Tiers
|
||||
|
||||
Paid plans include access to direct support from the Documenso team.
|
||||
|
||||
<Tabs items={['Standard Support', 'Platform and Above', 'Enterprise Support']}>
|
||||
<Tab value="Standard Support">
|
||||
|
||||
Available on paid subscription plans:
|
||||
|
||||
- Email support at support@documenso.com
|
||||
- Account and billing assistance
|
||||
- Technical guidance for Documenso Cloud usage
|
||||
- Response during business hours
|
||||
|
||||
</Tab>
|
||||
<Tab value="Platform and Above">
|
||||
|
||||
Available on [Platform plans](https://documen.so/platform-cta-pricing) and above:
|
||||
|
||||
- All standard support channels
|
||||
- Private Discord channel for direct communication with the Documenso team
|
||||
- Private Slack workspace for closer collaboration
|
||||
|
||||
</Tab>
|
||||
<Tab value="Enterprise Support">
|
||||
|
||||
Available with Enterprise subscriptions:
|
||||
|
||||
- All Platform support channels
|
||||
- Priority response times
|
||||
- Dedicated support contact
|
||||
- Assistance with deployment and integration
|
||||
- Custom configuration guidance
|
||||
- Scheduled calls for complex issues
|
||||
|
||||
Contact [sales](https://documen.so/enterprise) for Enterprise support options.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Callout type="info">
|
||||
If you prefer Discord or Slack, contact support to be invited to a private channel or workspace.
|
||||
</Callout>
|
||||
|
||||
## Self-Hosted Support
|
||||
|
||||
Self-hosted deployments have access to community support through Discord and GitHub. The following resources are available:
|
||||
|
||||
- [Self-Hosting Documentation](/docs/self-hosting) - Setup and configuration guides
|
||||
- [Troubleshooting Guide](/docs/self-hosting/maintenance/troubleshooting) - Common issues and solutions
|
||||
- [GitHub Discussions](https://github.com/documenso/documenso/discussions) - Community Q&A
|
||||
|
||||
<Callout type="info">
|
||||
Enterprise licenses for self-hosted deployments can include dedicated support. Contact
|
||||
[sales](https://documen.so/enterprise) for details.
|
||||
</Callout>
|
||||
|
||||
## Response Times
|
||||
|
||||
Response times depend on the support channel and plan:
|
||||
|
||||
| Plan | Channel | Target Response Time |
|
||||
| ------------------ | --------------------------- | ------------------------- |
|
||||
| Free / Self-Hosted | Community (Discord, GitHub) | Best effort |
|
||||
| Paid Plans | Email | 1-2 business days |
|
||||
| Enterprise | Priority Support | Within 4-8 business hours |
|
||||
|
||||
Response times are targets, not guarantees. Actual times may vary based on issue complexity and volume.
|
||||
|
||||
## What's Included
|
||||
|
||||
<Tabs items={['Included', 'Not included']}>
|
||||
<Tab value="Included">
|
||||
|
||||
- Guidance on Documenso features and functionality
|
||||
- Help with configuration and settings
|
||||
- Troubleshooting application errors
|
||||
- Account and billing questions (paid plans)
|
||||
- Bug reports and issue investigation
|
||||
- Upgrade assistance
|
||||
|
||||
</Tab>
|
||||
<Tab value="Not included">
|
||||
|
||||
- Custom development or code modifications
|
||||
- Third-party integration development
|
||||
- Infrastructure setup (servers, databases, networking)
|
||||
- Training sessions (available separately)
|
||||
- Support for modified or forked versions
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Callout type="info">
|
||||
Professional services for custom development, training, and dedicated implementation support are
|
||||
available separately. Contact [sales](https://documen.so/sales) for details.
|
||||
</Callout>
|
||||
|
||||
## Escalation Process
|
||||
|
||||
If your issue is not resolved through initial support channels:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Community support
|
||||
|
||||
If you don't receive a response on Discord or GitHub within a reasonable time, try the other channel or provide additional details.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Email support
|
||||
|
||||
Reply to your existing support thread with additional information. Avoid opening duplicate tickets.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Request escalation
|
||||
|
||||
For urgent issues on paid plans, request escalation in your support email.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Enterprise customers
|
||||
|
||||
Contact your dedicated support representative directly.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
For billing disputes or account issues, email support@documenso.com with your account details.
|
||||
|
||||
## Security Issues
|
||||
|
||||
Do not report security vulnerabilities through public channels (GitHub Issues, Discord, etc.).
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Email the security team
|
||||
|
||||
Email security@documenso.com.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Include details
|
||||
|
||||
Include a detailed description of the vulnerability.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Provide steps to reproduce
|
||||
|
||||
Provide steps to reproduce if possible.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Allow time for investigation
|
||||
|
||||
Allow time for investigation before public disclosure.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Documenso follows responsible disclosure practices. Security researchers who report valid vulnerabilities responsibly will be acknowledged.
|
||||
|
||||
## Contact
|
||||
|
||||
| Purpose | Contact |
|
||||
| ---------------- | -------------------------------------------------------------- |
|
||||
| General support | support@documenso.com |
|
||||
| Security issues | security@documenso.com |
|
||||
| Enterprise sales | [sales@documenso.com](https://documen.so/enterprise) |
|
||||
| Privacy requests | privacy@documenso.com |
|
||||
| Community | [Discord](https://documen.so/discord) |
|
||||
| Bug reports | [GitHub Issues](https://github.com/documenso/documenso/issues) |
|
||||
|
||||
## Related
|
||||
|
||||
- [Troubleshooting](/docs/self-hosting/maintenance/troubleshooting) - Self-hosted issue resolution
|
||||
- [Self-Hosting](/docs/self-hosting) - Deployment and configuration guides
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - Commercial licensing options
|
||||
- [Privacy Policy](/docs/policies/privacy) - How Documenso handles your data
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Terms of Service
|
||||
description: The terms that govern your use of Documenso.
|
||||
---
|
||||
|
||||
Our full Terms of Service are available at [documenso.com/terms](https://documenso.com/terms).
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: AI Recipient & Field Detection (Self-hosting)
|
||||
description: Configure Google Vertex AI so Documenso can detect recipients and fields automatically.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers how to enable the AI recipient and field detection features when you self-host Documenso.
|
||||
|
||||
## What This Enables
|
||||
|
||||
- Detect recipients from uploaded PDFs (roles, names, emails when present).
|
||||
- Detect and place fields (signature, initials, name, email, date, text, number, radio, checkbox) onto draft envelopes.
|
||||
- Built-in rate limits (3 requests per minute per IP) to prevent abuse.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Google Cloud project with the **Vertex AI API** enabled and billing active.
|
||||
- A **Vertex AI Express API key** with access to Gemini models (create via the [Vertex AI Express flow](https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview) and manage keys in [API keys](https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys)).
|
||||
- Documenso version that includes the AI detection feature and the corresponding database migration.
|
||||
|
||||
## Configure Environment Variables
|
||||
|
||||
Add these variables to your deployment `.env` (or secret manager):
|
||||
|
||||
```
|
||||
GOOGLE_VERTEX_PROJECT_ID="<your-gcp-project-id>"
|
||||
GOOGLE_VERTEX_API_KEY="<your-vertex-api-key>"
|
||||
# Optional, defaults to "global"
|
||||
GOOGLE_VERTEX_LOCATION="global"
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
Use a region close to your users if you need data residency considerations (e.g. `europe-west1`).
|
||||
If you omit the location, Documenso uses `global`. Not all models are available in every region;
|
||||
if a model is unavailable, switch to a supported region.
|
||||
</Callout>
|
||||
|
||||
## Deploy with the Published Container
|
||||
|
||||
- Use the official Documenso image (DockerHub or GHCR) and supply the Vertex env vars above.
|
||||
- Ensure migrations run on startup (the container runs `prisma migrate deploy` in production mode).
|
||||
- Restart the container after adding or changing Vertex env vars.
|
||||
|
||||
## Enable the Feature in Documenso
|
||||
|
||||
Once the service is running with the Vertex env vars:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Organisation settings
|
||||
|
||||
Go to **Settings** → **Document Preferences** → **AI Features** and set to **Enabled**.
|
||||
|
||||
Teams that inherit organisation defaults will get this automatically.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Team settings
|
||||
|
||||
If a team overrides organisation defaults:
|
||||
|
||||
- Go to **Team Settings** → **Document Preferences** → **AI Features**
|
||||
- Choose **Enabled**, or **Inherit** to follow the organisation
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Verify in the editor
|
||||
|
||||
Open a draft envelope and check:
|
||||
|
||||
- **Recipients**: sparkle button for AI detection is visible
|
||||
- **Fields**: **Detect with AI** is available
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Too many requests">
|
||||
Wait a minute or two and retry (rate limit is 3/min per IP).
|
||||
</Accordion>
|
||||
<Accordion title="AI options hidden">
|
||||
Ensure the env vars are set, the server was restarted after setting them, and
|
||||
`aiFeaturesEnabled` is enabled at organisation/team level.
|
||||
</Accordion>
|
||||
<Accordion title="Detection fails immediately">
|
||||
Confirm the Vertex API key is valid and the project has Vertex AI enabled. Check server logs for
|
||||
status codes from Vertex.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
If issues persist, recheck env vars, restart the service, and confirm the Prisma migration was applied.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [AI Detection (User Guide)](/docs/users/documents/advanced/ai-detection) - How to use AI detection features
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - All configuration options
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Advanced
|
||||
description: Optional configuration for OAuth providers, AI features, and other advanced settings.
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="OAuth Providers"
|
||||
description="Configure Google, GitHub, and OIDC authentication."
|
||||
href="/docs/self-hosting/configuration/advanced/oauth-providers"
|
||||
/>
|
||||
<Card
|
||||
title="AI Features"
|
||||
description="Enable AI-powered recipient and field detection."
|
||||
href="/docs/self-hosting/configuration/advanced/ai-features"
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Advanced",
|
||||
"pages": ["oauth-providers", "ai-features"]
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Setting up OAuth Providers
|
||||
description: Learn how to set up OAuth providers for your own instance of Documenso.
|
||||
---
|
||||
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Google OAuth (Gmail)
|
||||
|
||||
To use Google OAuth, you will need to create a Google Cloud Platform project and enable the Google Identity and Access Management (IAM) API. You will also need to create a new OAuth client ID and download the client secret.
|
||||
|
||||
### Create and configure a new OAuth client ID
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Open Google Cloud Console
|
||||
|
||||
Go to the [Google Cloud Platform Console](https://console.cloud.google.com/) and select a project or create a new one from the projects list.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Open APIs & services
|
||||
|
||||
From the console's left side menu, select **APIs & services** (if not already open).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Open Credentials
|
||||
|
||||
On the left, click **Credentials**.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create OAuth client ID
|
||||
|
||||
Click **New Credentials**, then select **OAuth client ID**. When prompted, select **Web application**.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Name and create the client
|
||||
|
||||
Enter a name for your client ID and click **Create**. Click the download button to download the client secret.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Set authorized origins and redirect URIs
|
||||
|
||||
Set:
|
||||
|
||||
- **Authorized JavaScript origins**: `https://<documenso-domain>`
|
||||
- **Authorized redirect URIs**: `https://<documenso-domain>/api/auth/callback/google`
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Set environment variables
|
||||
|
||||
In your Documenso environment, set:
|
||||
|
||||
```
|
||||
NEXT_PRIVATE_GOOGLE_CLIENT_ID=<your-client-id>
|
||||
NEXT_PRIVATE_GOOGLE_CLIENT_SECRET=<your-client-secret>
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Finally verify sign-in with Google by signing in with your Google account and checking the email address in your profile.
|
||||
|
||||
## Microsoft OAuth (Azure AD)
|
||||
|
||||
To use Microsoft OAuth, you will need to create an Azure AD application registration in the Microsoft Azure portal. This will allow users to sign in with their Microsoft accounts.
|
||||
|
||||
### Create and configure a new Azure AD application
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Open Azure Portal
|
||||
|
||||
Go to the [Azure Portal](https://portal.azure.com/) and navigate to **Azure Active Directory** (or **Microsoft Entra ID** in newer portals).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Start app registration
|
||||
|
||||
In the left sidebar, click **App registrations**, then **New registration**.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Name and choose account types
|
||||
|
||||
Enter a name (e.g. "Documenso").
|
||||
|
||||
Under **Supported account types**, select both:
|
||||
|
||||
- Accounts in any organizational directory (Any Azure AD directory - Multitenant)
|
||||
- Personal Microsoft accounts (e.g. Skype, Xbox)
|
||||
|
||||
This allows any Microsoft account to sign in.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Set redirect URI and register
|
||||
|
||||
Under **Redirect URI**, select **Web** and enter:
|
||||
|
||||
```bash
|
||||
https://<documenso-domain>/api/auth/callback/microsoft
|
||||
```
|
||||
|
||||
Click **Register**.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Configure the application
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Copy the Application (client) ID
|
||||
|
||||
After registration you're on the app overview page. Copy the **Application (client) ID** for `NEXT_PRIVATE_MICROSOFT_CLIENT_ID`.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create a client secret
|
||||
|
||||
- In the left sidebar, click **Certificates & secrets**
|
||||
- Under **Client secrets**, click **New client secret**
|
||||
- Add a description and select an expiration period, then click **Add**
|
||||
- Copy the **Value** (not the Secret ID): this is your `NEXT_PRIVATE_MICROSOFT_CLIENT_SECRET`
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Set environment variables
|
||||
|
||||
In your Documenso environment, set:
|
||||
|
||||
```
|
||||
NEXT_PRIVATE_MICROSOFT_CLIENT_ID=<your-application-client-id>
|
||||
NEXT_PRIVATE_MICROSOFT_CLIENT_SECRET=<your-client-secret-value>
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - All configuration options
|
||||
- [Single Sign-On](/docs/users/organisations/single-sign-on) - Configure SSO for your organisation
|
||||
- [Microsoft Entra ID](/docs/users/organisations/single-sign-on/microsoft-entra-id) - Detailed Entra ID setup guide
|
||||
@@ -0,0 +1,490 @@
|
||||
---
|
||||
title: Database Configuration
|
||||
description: Configure PostgreSQL connection strings, pooling, SSL, migrations, and performance tuning for your Documenso deployment.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Supported Databases
|
||||
|
||||
Documenso requires **PostgreSQL 14 or later**. No other databases are supported.
|
||||
|
||||
PostgreSQL provides the reliability, performance, and feature set required for document signing workflows, including:
|
||||
|
||||
- ACID compliance for transaction integrity
|
||||
- JSON support for flexible metadata storage
|
||||
- Full-text search capabilities
|
||||
- Robust backup and replication options
|
||||
|
||||
## Connection String Format
|
||||
|
||||
PostgreSQL connection strings follow this format:
|
||||
|
||||
```
|
||||
postgresql://[user]:[password]@[host]:[port]/[database]?[parameters]
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
| Component | Description | Example |
|
||||
| ------------ | ------------------------------- | ----------------- |
|
||||
| `user` | Database username | `documenso` |
|
||||
| `password` | Database password (URL-encoded) | `secretpass` |
|
||||
| `host` | Database server hostname or IP | `localhost` |
|
||||
| `port` | Database port | `5432` |
|
||||
| `database` | Database name | `documenso` |
|
||||
| `parameters` | Additional connection options | `sslmode=require` |
|
||||
|
||||
### Examples
|
||||
|
||||
**Local development:**
|
||||
|
||||
```
|
||||
postgresql://documenso:password@localhost:5432/documenso
|
||||
```
|
||||
|
||||
**Remote server with SSL:**
|
||||
|
||||
```
|
||||
postgresql://documenso:password@db.example.com:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
**With special characters in password:**
|
||||
|
||||
URL-encode special characters in passwords. For example, `p@ss#word` becomes `p%40ss%23word`:
|
||||
|
||||
```
|
||||
postgresql://documenso:p%40ss%23word@localhost:5432/documenso
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Documenso uses two database connection variables:
|
||||
|
||||
| Variable | Purpose |
|
||||
| ---------------------------------- | --------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_DATABASE_URL` | Primary connection for application queries |
|
||||
| `NEXT_PRIVATE_DIRECT_DATABASE_URL` | Direct connection for migrations and schema changes |
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
When not using a connection pooler, set both variables to the same value:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://user:password@host:5432/documenso
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://user:password@host:5432/documenso
|
||||
```
|
||||
|
||||
### Automatic Detection
|
||||
|
||||
Documenso automatically detects common database environment variable formats used by hosting providers:
|
||||
|
||||
| Provider Variable | Maps To |
|
||||
| -------------------------- | ---------------------------------- |
|
||||
| `DATABASE_URL` | `NEXT_PRIVATE_DATABASE_URL` |
|
||||
| `POSTGRES_URL` | `NEXT_PRIVATE_DATABASE_URL` |
|
||||
| `POSTGRES_PRISMA_URL` | `NEXT_PRIVATE_DATABASE_URL` |
|
||||
| `DATABASE_URL_UNPOOLED` | `NEXT_PRIVATE_DIRECT_DATABASE_URL` |
|
||||
| `POSTGRES_URL_NON_POOLING` | `NEXT_PRIVATE_DIRECT_DATABASE_URL` |
|
||||
|
||||
If your hosting provider sets these variables, Documenso will use them automatically.
|
||||
|
||||
## Connection Pooling
|
||||
|
||||
Connection pooling improves performance by reusing database connections instead of creating new ones for each request.
|
||||
|
||||
### When to Use Pooling
|
||||
|
||||
Use connection pooling when:
|
||||
|
||||
- Running multiple application instances
|
||||
- Deploying to serverless environments
|
||||
- Handling high concurrent request volumes
|
||||
- Your database has connection limits
|
||||
|
||||
### PgBouncer Configuration
|
||||
|
||||
When using PgBouncer or similar poolers, configure two connection strings:
|
||||
|
||||
1. **Pooled connection** for application queries
|
||||
2. **Direct connection** for migrations (bypasses the pooler)
|
||||
|
||||
```bash
|
||||
# Pooled connection (through PgBouncer)
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://user:password@pooler-host:6432/documenso?pgbouncer=true
|
||||
|
||||
# Direct connection (bypasses PgBouncer)
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://user:password@db-host:5432/documenso
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
Migrations must use a direct connection. Running migrations through a connection pooler will fail.
|
||||
</Callout>
|
||||
|
||||
### Prisma Connection Pool
|
||||
|
||||
Documenso uses Prisma, which maintains its own connection pool. Configure pool size with connection string parameters:
|
||||
|
||||
```
|
||||
postgresql://user:password@host:5432/documenso?connection_limit=10&pool_timeout=30
|
||||
```
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| ------------------ | ---------------------------------------- | ------- |
|
||||
| `connection_limit` | Maximum connections in the pool | 10 |
|
||||
| `pool_timeout` | Seconds to wait for available connection | 10 |
|
||||
|
||||
## SSL/TLS Connections
|
||||
|
||||
### Enabling SSL
|
||||
|
||||
Add SSL parameters to your connection string:
|
||||
|
||||
```
|
||||
postgresql://user:password@host:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
### SSL Modes
|
||||
|
||||
| Mode | Description |
|
||||
| ------------- | --------------------------------------------------- |
|
||||
| `disable` | No SSL (not recommended for production) |
|
||||
| `allow` | Try non-SSL first, fall back to SSL |
|
||||
| `prefer` | Try SSL first, fall back to non-SSL |
|
||||
| `require` | Require SSL, but don't verify certificate |
|
||||
| `verify-ca` | Require SSL and verify server certificate |
|
||||
| `verify-full` | Require SSL, verify certificate, and check hostname |
|
||||
|
||||
For production, use `require` at minimum. Use `verify-full` when your CA certificate is available.
|
||||
|
||||
### Custom Certificates
|
||||
|
||||
When connecting to databases with self-signed or private CA certificates:
|
||||
|
||||
```
|
||||
postgresql://user:password@host:5432/documenso?sslmode=verify-full&sslrootcert=/path/to/ca.crt
|
||||
```
|
||||
|
||||
For Docker deployments, mount the certificate file:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-v /path/to/ca.crt:/etc/ssl/certs/db-ca.crt:ro \
|
||||
-e NEXT_PRIVATE_DATABASE_URL="postgresql://user:password@host:5432/documenso?sslmode=verify-full&sslrootcert=/etc/ssl/certs/db-ca.crt" \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
## Running Migrations
|
||||
|
||||
Database migrations update your schema when upgrading Documenso.
|
||||
|
||||
### Automatic Migrations
|
||||
|
||||
When running Documenso via Docker, migrations run automatically on container startup. No manual intervention is required.
|
||||
|
||||
### Manual Migrations
|
||||
|
||||
For manual deployments or troubleshooting:
|
||||
|
||||
```bash
|
||||
# Apply pending migrations
|
||||
npm run prisma:migrate-deploy
|
||||
|
||||
# Or using npx directly
|
||||
npx prisma migrate deploy
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
Always back up your database before running migrations, especially for major version upgrades.
|
||||
</Callout>
|
||||
|
||||
### Migration Commands
|
||||
|
||||
| Command | Purpose |
|
||||
| ----------------------- | ----------------------------------------- |
|
||||
| `prisma:migrate-deploy` | Apply pending migrations (production) |
|
||||
| `prisma:migrate-dev` | Create and apply migrations (development) |
|
||||
| `prisma:migrate-reset` | Reset database and apply all migrations |
|
||||
|
||||
### Troubleshooting Migrations
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Migration failed midway">
|
||||
Check the `_prisma_migrations` table:
|
||||
|
||||
```sql
|
||||
SELECT * FROM _prisma_migrations WHERE finished_at IS NULL;
|
||||
```
|
||||
|
||||
To retry, fix the underlying issue (disk space, permissions, etc.), mark as rolled back:
|
||||
|
||||
```sql
|
||||
UPDATE _prisma_migrations SET rolled_back_at = NOW() WHERE migration_name = 'failed_migration_name';
|
||||
```
|
||||
|
||||
Run migrations again.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Connection timeout during migration">
|
||||
Use the direct database URL and increase timeout:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL="postgresql://user:password@host:5432/documenso?connect_timeout=60"
|
||||
```
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Managed Database Services
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Supabase">
|
||||
Supabase provides PostgreSQL with built-in connection pooling via Supavisor.
|
||||
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
# Pooled connection (Session mode - port 5432)
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres
|
||||
|
||||
# Direct connection for migrations (port 5432, direct host)
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres
|
||||
```
|
||||
|
||||
Find your connection strings in the Supabase dashboard under **Settings > Database > Connection string**.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Neon">
|
||||
Neon provides serverless PostgreSQL with automatic scaling.
|
||||
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
# Pooled connection
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://[user]:[password]@[endpoint]-pooler.region.aws.neon.tech/documenso?sslmode=require
|
||||
|
||||
# Direct connection for migrations
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://[user]:[password]@[endpoint].region.aws.neon.tech/documenso?sslmode=require
|
||||
```
|
||||
|
||||
The pooler endpoint includes `-pooler` in the hostname.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="AWS RDS">
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://documenso:[password]@your-instance.region.rds.amazonaws.com:5432/documenso?sslmode=require
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://documenso:[password]@your-instance.region.rds.amazonaws.com:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
**Recommended settings:**
|
||||
|
||||
- Instance class: `db.t3.medium` or larger for production
|
||||
- Storage: General Purpose SSD (gp3), minimum 20GB
|
||||
- Enable automated backups with 7+ day retention
|
||||
- Enable Multi-AZ for high availability
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Google Cloud SQL">
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://documenso:[password]@/documenso?host=/cloudsql/[project]:[region]:[instance]
|
||||
```
|
||||
|
||||
When connecting via Cloud SQL Proxy, use Unix socket connections.
|
||||
|
||||
For public IP connections:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://documenso:[password]@[public-ip]:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Azure Database for PostgreSQL">
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://documenso@[server-name]:[password]@[server-name].postgres.database.azure.com:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
Note: Azure requires the username format `user@servername`.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="DigitalOcean Managed Databases">
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://doadmin:[password]@[cluster-host]:25060/documenso?sslmode=require
|
||||
```
|
||||
|
||||
DigitalOcean uses port 25060 by default and requires SSL.
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Backup Recommendations
|
||||
|
||||
### Backup Strategies
|
||||
|
||||
| Strategy | Frequency | Retention | Use Case |
|
||||
| ---------------------- | ------------ | ----------- | ---------------------- |
|
||||
| Automated snapshots | Daily | 7-30 days | Point-in-time recovery |
|
||||
| Logical backups | Daily/Weekly | 30-90 days | Long-term retention |
|
||||
| Continuous replication | Real-time | 24-72 hours | Disaster recovery |
|
||||
|
||||
### PostgreSQL Backup Commands
|
||||
|
||||
**Create a logical backup:**
|
||||
|
||||
```bash
|
||||
pg_dump -h host -U user -d documenso -F c -f documenso_backup.dump
|
||||
```
|
||||
|
||||
**Restore from backup:**
|
||||
|
||||
```bash
|
||||
pg_restore -h host -U user -d documenso -c documenso_backup.dump
|
||||
```
|
||||
|
||||
### Managed Service Backups
|
||||
|
||||
Most managed database services provide automated backups:
|
||||
|
||||
<Tabs items={['Supabase', 'Neon', 'AWS RDS', 'Google Cloud SQL', 'Azure']}>
|
||||
<Tab value="Supabase">Daily backups with point-in-time recovery (Pro plan)</Tab>
|
||||
<Tab value="Neon">Automatic branching for instant recovery</Tab>
|
||||
<Tab value="AWS RDS">Automated backups with configurable retention</Tab>
|
||||
<Tab value="Google Cloud SQL">Automated and on-demand backups</Tab>
|
||||
<Tab value="Azure">Automatic backups with geo-redundancy options</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Callout type="warn">
|
||||
Always test your backup restoration process. Untested backups may not work when needed.
|
||||
</Callout>
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### PostgreSQL Configuration
|
||||
|
||||
Key parameters for Documenso workloads:
|
||||
|
||||
| Parameter | Recommended Value | Description |
|
||||
| ---------------------- | ----------------- | ------------------------------------- |
|
||||
| `shared_buffers` | 25% of RAM | Memory for caching data |
|
||||
| `effective_cache_size` | 75% of RAM | Planner's estimate of available cache |
|
||||
| `work_mem` | 64MB-256MB | Memory per sort/hash operation |
|
||||
| `maintenance_work_mem` | 512MB-1GB | Memory for maintenance operations |
|
||||
| `max_connections` | 100-200 | Maximum concurrent connections |
|
||||
|
||||
### Connection Limits
|
||||
|
||||
Calculate your connection limit:
|
||||
|
||||
```
|
||||
max_connections = (application_instances × connection_pool_size) + admin_overhead
|
||||
```
|
||||
|
||||
Example: 3 app instances with pool size 10 = `(3 × 10) + 10 = 40` connections minimum.
|
||||
|
||||
### Indexing
|
||||
|
||||
Documenso includes necessary indexes by default. Additional indexes may help for:
|
||||
|
||||
- Custom reporting queries
|
||||
- High-volume document searches
|
||||
- Audit log analysis
|
||||
|
||||
Check for slow queries:
|
||||
|
||||
```sql
|
||||
SELECT query, calls, mean_time, total_time
|
||||
FROM pg_stat_statements
|
||||
ORDER BY total_time DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
Monitor these metrics:
|
||||
|
||||
| Metric | Warning Threshold | Action |
|
||||
| -------------------- | ----------------- | ------------------------------- |
|
||||
| Connection usage | > 80% | Increase limits or add pooling |
|
||||
| Disk usage | > 80% | Add storage or archive old data |
|
||||
| Cache hit ratio | < 95% | Increase `shared_buffers` |
|
||||
| Long-running queries | > 30 seconds | Optimize query or add indexes |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Connection refused">
|
||||
Causes:
|
||||
- PostgreSQL not running
|
||||
- Incorrect host or port
|
||||
- Firewall blocking
|
||||
|
||||
Solutions:
|
||||
- Verify PostgreSQL is running with `pg_isready -h host -p 5432`
|
||||
- Check the connection string
|
||||
- Verify firewall rules
|
||||
with `pg_isready -h host -p 5432`, check the connection string, verify firewall rules.
|
||||
</Accordion>
|
||||
<Accordion title="Authentication failed">
|
||||
Causes:
|
||||
- Incorrect password
|
||||
- User doesn't exist
|
||||
- Password contains special characters
|
||||
|
||||
Solutions:
|
||||
- Reset password with `ALTER USER documenso WITH PASSWORD 'newpassword';`
|
||||
- Verify credentials in `pg_hba.conf`
|
||||
- Verify URL-encoded special characters
|
||||
</Accordion>
|
||||
<Accordion title="SSL connection required">
|
||||
Add SSL mode to your connection string:
|
||||
|
||||
```bash
|
||||
postgresql://user:password@host:5432/documenso?sslmode=require
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Too many connections">
|
||||
Causes:
|
||||
- Connection pool exhausted
|
||||
- Connections not released
|
||||
- Multiple instances exceeding limits
|
||||
|
||||
Solutions:
|
||||
- Reduce `connection_limit`
|
||||
- Increase `max_connections`
|
||||
- Implement PgBouncer
|
||||
</Accordion>
|
||||
<Accordion title="Database does not exist">
|
||||
Create the database:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE documenso;
|
||||
GRANT ALL PRIVILEGES ON DATABASE documenso TO documenso;
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Permission denied">
|
||||
Grant permissions:
|
||||
|
||||
```sql
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO documenso;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO documenso;
|
||||
```
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Complete configuration reference
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - Set up document storage
|
||||
- [Backups](/docs/self-hosting/maintenance/backups) - Backup strategies and procedures
|
||||
- [Upgrades](/docs/self-hosting/maintenance/upgrades) - Upgrade procedures and migration handling
|
||||
@@ -0,0 +1,473 @@
|
||||
---
|
||||
title: Email Configuration
|
||||
description: Configure email delivery for notifications and document signing invitations.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Supported Email Transports
|
||||
|
||||
Documenso supports four email transport methods:
|
||||
|
||||
| Transport | Use Case | Configuration Complexity |
|
||||
| -------------- | ------------------------------------- | ------------------------ |
|
||||
| `smtp-auth` | Standard SMTP with username/password | Low |
|
||||
| `smtp-api` | SMTP with API key authentication | Low |
|
||||
| `resend` | Resend.com email API | Low |
|
||||
| `mailchannels` | MailChannels API (Cloudflare Workers) | Medium |
|
||||
|
||||
Select a transport by setting the `NEXT_PRIVATE_SMTP_TRANSPORT` environment variable. The default is `smtp-auth`.
|
||||
|
||||
## Sender Configuration
|
||||
|
||||
All transports require sender configuration:
|
||||
|
||||
| Variable | Description | Required |
|
||||
| -------------------------------- | --------------------------------- | -------- |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | Email address shown as the sender | Yes |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_NAME` | Display name shown as the sender | Yes |
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@example.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
The sender address must be authorized to send from your email provider. Using an unauthorized
|
||||
address will cause delivery failures.
|
||||
</Callout>
|
||||
|
||||
## SMTP Configuration
|
||||
|
||||
SMTP is the most common email transport. Documenso supports two SMTP authentication methods.
|
||||
|
||||
### SMTP with Username/Password (smtp-auth)
|
||||
|
||||
Use this for standard SMTP servers that authenticate with username and password.
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------- | ----------------------------------------- | ----------- |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Set to `smtp-auth` | `smtp-auth` |
|
||||
| `NEXT_PRIVATE_SMTP_HOST` | SMTP server hostname | `127.0.0.1` |
|
||||
| `NEXT_PRIVATE_SMTP_PORT` | SMTP server port | `587` |
|
||||
| `NEXT_PRIVATE_SMTP_USERNAME` | Authentication username | |
|
||||
| `NEXT_PRIVATE_SMTP_PASSWORD` | Authentication password | |
|
||||
| `NEXT_PRIVATE_SMTP_SECURE` | Use TLS on connection (`true` or `false`) | `false` |
|
||||
| `NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS` | Disable TLS entirely (not recommended) | `false` |
|
||||
| `NEXT_PRIVATE_SMTP_SERVICE` | Nodemailer service preset (e.g., `gmail`) | |
|
||||
|
||||
**Example Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.example.com"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-username"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-password"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@example.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
### Common SMTP Provider Examples
|
||||
|
||||
**Gmail / Google Workspace:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.gmail.com"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-email@gmail.com"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-app-password"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="your-email@gmail.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
Gmail requires an App Password when 2FA is enabled. Generate one at [Google Account
|
||||
Security](https://myaccount.google.com/apppasswords).
|
||||
</Callout>
|
||||
|
||||
**Microsoft 365 / Outlook:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.office365.com"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-email@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-password"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="your-email@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
**Amazon SES:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="email-smtp.us-east-1.amazonaws.com"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-ses-smtp-username"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-ses-smtp-password"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
**Postmark:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.postmarkapp.com"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-postmark-server-api-token"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-postmark-server-api-token"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
### SMTP with API Key (smtp-api)
|
||||
|
||||
Some providers use API key authentication instead of username/password. Use this transport for services like SendGrid.
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------- | --------------------- | -------- |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Set to `smtp-api` | |
|
||||
| `NEXT_PRIVATE_SMTP_HOST` | SMTP server hostname | |
|
||||
| `NEXT_PRIVATE_SMTP_PORT` | SMTP server port | `587` |
|
||||
| `NEXT_PRIVATE_SMTP_APIKEY_USER` | API key username | `apikey` |
|
||||
| `NEXT_PRIVATE_SMTP_APIKEY` | API key value | |
|
||||
| `NEXT_PRIVATE_SMTP_SECURE` | Use TLS on connection | `false` |
|
||||
|
||||
**SendGrid Example:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-api"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.sendgrid.net"
|
||||
NEXT_PRIVATE_SMTP_PORT="587"
|
||||
NEXT_PRIVATE_SMTP_APIKEY_USER="apikey"
|
||||
NEXT_PRIVATE_SMTP_APIKEY="SG.xxxxxxxxxxxxxxxxxxxx"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
## Resend Configuration
|
||||
|
||||
[Resend](https://resend.com) provides a developer-friendly email API with excellent deliverability.
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------------- | ------------------- |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Set to `resend` |
|
||||
| `NEXT_PRIVATE_RESEND_API_KEY` | Your Resend API key |
|
||||
|
||||
**Example Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="resend"
|
||||
NEXT_PRIVATE_RESEND_API_KEY="re_xxxxxxxxxxxxxxxxxxxx"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
**Setup Steps:**
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Create an account
|
||||
|
||||
Sign up at [resend.com](https://resend.com).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Add and verify your sending domain
|
||||
|
||||
Configure your domain in the Resend dashboard so you can send from your own address.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create an API key
|
||||
|
||||
Generate an API key in the Resend dashboard for Documenso to use.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure environment variables
|
||||
|
||||
Set the variables in the table above, including `NEXT_PRIVATE_RESEND_API_KEY` and your sender address.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## MailChannels Configuration
|
||||
|
||||
[MailChannels](https://www.mailchannels.com) is an email delivery service often used with Cloudflare Workers.
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------------------------- | -------------------------------------- | ----------------------------------------- |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Set to `mailchannels` | |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_API_KEY` | API key for authentication | |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_ENDPOINT` | Custom API endpoint (for proxy setups) | `https://api.mailchannels.net/tx/v1/send` |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN` | Domain for DKIM signing | |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR` | DKIM selector | |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY` | DKIM private key for signing | |
|
||||
|
||||
**Example Configuration:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="mailchannels"
|
||||
NEXT_PRIVATE_MAILCHANNELS_API_KEY="your-api-key"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
**With DKIM Signing:**
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="mailchannels"
|
||||
NEXT_PRIVATE_MAILCHANNELS_API_KEY="your-api-key"
|
||||
NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN="yourdomain.com"
|
||||
NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR="mailchannels"
|
||||
NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@yourdomain.com"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
```
|
||||
|
||||
## Email Templates
|
||||
|
||||
Documenso uses React Email templates for all outgoing emails. The templates are located in `packages/email/templates/` and include:
|
||||
|
||||
| Template | Purpose |
|
||||
| ------------------------- | ----------------------------------------- |
|
||||
| `document-invite.tsx` | Signing invitation sent to recipients |
|
||||
| `document-completed.tsx` | Notification when all parties have signed |
|
||||
| `document-pending.tsx` | Reminder for pending signatures |
|
||||
| `document-cancel.tsx` | Notification when a document is cancelled |
|
||||
| `document-rejected.tsx` | Notification when a recipient rejects |
|
||||
| `confirm-email.tsx` | Email verification for new accounts |
|
||||
| `forgot-password.tsx` | Password reset request |
|
||||
| `reset-password.tsx` | Password reset confirmation |
|
||||
| `organisation-invite.tsx` | Team/organisation invitation |
|
||||
|
||||
### Branding Customization
|
||||
|
||||
Email templates support branding customization through organisation settings. When branding is enabled:
|
||||
|
||||
- Custom logo replaces the Documenso logo
|
||||
- Branding colors are applied to email elements
|
||||
|
||||
Configure branding in the application under **Organisation Settings > Preferences > Branding**.
|
||||
|
||||
<Callout type="info">
|
||||
Template modifications require rebuilding the application. For simple customizations, use the
|
||||
branding settings instead.
|
||||
</Callout>
|
||||
|
||||
## Testing Email Delivery
|
||||
|
||||
### Verify Configuration
|
||||
|
||||
Test your email configuration by creating an account or resetting a password. These actions trigger emails that confirm delivery is working.
|
||||
|
||||
### Using a Test SMTP Server
|
||||
|
||||
For development or testing, use a local SMTP server like [Mailhog](https://github.com/mailhog/MailHog) or [Mailpit](https://github.com/axllent/mailpit):
|
||||
|
||||
```bash
|
||||
# Using Docker
|
||||
docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
|
||||
```
|
||||
|
||||
Configure Documenso to use the test server:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="localhost"
|
||||
NEXT_PRIVATE_SMTP_PORT="1025"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="test@localhost"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso Test"
|
||||
```
|
||||
|
||||
View sent emails at `http://localhost:8025`.
|
||||
|
||||
### Checking Logs
|
||||
|
||||
Email sending errors appear in the application logs. Check container or server logs for messages containing `mailer` or `email`:
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker logs documenso 2>&1 | grep -i email
|
||||
|
||||
# Docker Compose
|
||||
docker compose logs app 2>&1 | grep -i email
|
||||
```
|
||||
|
||||
## Email Deliverability
|
||||
|
||||
### SPF Configuration
|
||||
|
||||
SPF (Sender Policy Framework) authorizes servers to send email for your domain. Add a TXT record to your DNS:
|
||||
|
||||
```
|
||||
v=spf1 include:_spf.example.com ~all
|
||||
```
|
||||
|
||||
Replace `_spf.example.com` with your email provider's SPF include. Common examples:
|
||||
|
||||
| Provider | SPF Include |
|
||||
| ------------- | ------------------------------------ |
|
||||
| Gmail | `include:_spf.google.com` |
|
||||
| Microsoft 365 | `include:spf.protection.outlook.com` |
|
||||
| SendGrid | `include:sendgrid.net` |
|
||||
| Amazon SES | `include:amazonses.com` |
|
||||
| Resend | `include:_spf.resend.com` |
|
||||
| Postmark | `include:spf.mtasv.net` |
|
||||
|
||||
### DKIM Configuration
|
||||
|
||||
DKIM (DomainKeys Identified Mail) adds a digital signature to emails. Configuration varies by provider:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Generate DKIM keys
|
||||
|
||||
Create DKIM keys in your email provider's dashboard.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Add the TXT record to DNS
|
||||
|
||||
Add the provided TXT record to your DNS.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Enable DKIM signing
|
||||
|
||||
Enable DKIM signing in your provider settings.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
For MailChannels, configure DKIM directly in Documenso using the environment variables described above.
|
||||
|
||||
### DMARC Configuration
|
||||
|
||||
DMARC (Domain-based Message Authentication, Reporting, and Conformance) tells receiving servers how to handle emails that fail SPF or DKIM checks. Add a TXT record:
|
||||
|
||||
```
|
||||
_dmarc.yourdomain.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"
|
||||
```
|
||||
|
||||
DMARC policies:
|
||||
|
||||
| Policy | Behavior |
|
||||
| -------------- | ------------------------------ |
|
||||
| `p=none` | Monitor only, no action taken |
|
||||
| `p=quarantine` | Move failing emails to spam |
|
||||
| `p=reject` | Reject failing emails entirely |
|
||||
|
||||
Start with `p=none` to monitor, then move to stricter policies once deliverability is confirmed.
|
||||
|
||||
## Common Issues
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Emails Not Sending">
|
||||
**Symptoms:** No emails received, no errors in logs.
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
- Incorrect SMTP credentials
|
||||
- Firewall blocking outbound SMTP ports
|
||||
- From address not authorized
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Verify credentials with your email provider
|
||||
- Check that ports 25, 465, or 587 are open outbound
|
||||
- Verify the from address is authorized to send
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Connection Timeout">
|
||||
**Symptoms:** Error logs show connection timeout to SMTP server.
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
- Incorrect hostname or port
|
||||
- Firewall blocking connection
|
||||
- SMTP server down
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Verify hostname and port match provider documentation
|
||||
- Test connectivity: `telnet smtp.example.com 587`
|
||||
- Check provider status page
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Authentication Failed">
|
||||
**Symptoms:** Error logs show authentication failure.
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
- Wrong username or password
|
||||
- Account requires app-specific password
|
||||
- Account security restrictions
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Double-check credentials
|
||||
- Generate an app password if using Gmail or Microsoft with 2FA
|
||||
- Check for account security alerts from your provider
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Emails Going to Spam">
|
||||
**Symptoms:** Emails deliver but land in spam folders.
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
- Missing or incorrect SPF record
|
||||
- Missing DKIM signature
|
||||
- No DMARC policy
|
||||
- Poor sender reputation
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Configure SPF, DKIM, and DMARC as described above
|
||||
- Use a reputable email provider
|
||||
- Verify your domain with your email provider
|
||||
- Monitor deliverability with tools like [mail-tester.com](https://www.mail-tester.com)
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="TLS/SSL Errors">
|
||||
**Symptoms:** Errors mentioning TLS, SSL, or certificate issues.
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
- Server requires TLS but `NEXT_PRIVATE_SMTP_SECURE` is false
|
||||
- Self-signed certificate on SMTP server
|
||||
- Port mismatch (465 for implicit TLS, 587 for STARTTLS)
|
||||
|
||||
**Solutions:**
|
||||
|
||||
- Set `NEXT_PRIVATE_SMTP_SECURE="true"` for port 465
|
||||
- Keep `NEXT_PRIVATE_SMTP_SECURE="false"` for port 587 (STARTTLS)
|
||||
- As a last resort, set `NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS="true"` (not recommended for production)
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Complete configuration reference
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - Set up document storage
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Configure document signing
|
||||
- [Troubleshooting](/docs/self-hosting/maintenance/troubleshooting) - Common issues and solutions
|
||||
@@ -0,0 +1,339 @@
|
||||
---
|
||||
title: Environment Variables
|
||||
description: Complete reference for all environment variables used to configure a self-hosted Documenso instance.
|
||||
---
|
||||
|
||||
## Required Variables
|
||||
|
||||
These variables must be set for Documenso to function:
|
||||
|
||||
| Variable | Description |
|
||||
| --------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `NEXTAUTH_SECRET` | Secret key for NextAuth.js encryption and signing. Generate with `openssl rand -base64 32` |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_KEY` | Primary encryption key for symmetric encryption (minimum 32 characters) |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY` | Secondary encryption key for symmetric encryption (minimum 32 characters) |
|
||||
| `NEXT_PUBLIC_WEBAPP_URL` | Public URL of your Documenso instance (e.g., `https://sign.example.com`) |
|
||||
| `NEXT_PRIVATE_DATABASE_URL` | PostgreSQL connection URL with connection pooling |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | Email address used as the sender for all outgoing emails |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_NAME` | Display name for the sender of outgoing emails |
|
||||
|
||||
---
|
||||
|
||||
## Server Configuration
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ---------------------------------- | ------------------------------------------------------------ | -------------------------------- |
|
||||
| `PORT` | Port the server listens on | `3000` |
|
||||
| `NEXT_PRIVATE_INTERNAL_WEBAPP_URL` | Internal URL for the app to request itself (background jobs) | Same as `NEXT_PUBLIC_WEBAPP_URL` |
|
||||
|
||||
---
|
||||
|
||||
## Database Configuration
|
||||
|
||||
Documenso requires PostgreSQL 14 or higher.
|
||||
|
||||
| Variable | Required | Description |
|
||||
| ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------ |
|
||||
| `NEXT_PRIVATE_DATABASE_URL` | Yes | PostgreSQL connection URL. Supports connection pooling (e.g., PgBouncer) |
|
||||
| `NEXT_PRIVATE_DIRECT_DATABASE_URL` | When pooling | Direct PostgreSQL URL for migrations. Defaults to `NEXT_PRIVATE_DATABASE_URL` when not set |
|
||||
|
||||
**Connection string format:**
|
||||
|
||||
```
|
||||
postgres://user:password@host:port/database
|
||||
```
|
||||
|
||||
For detailed database setup, see [Database Configuration](/docs/self-hosting/configuration/database).
|
||||
|
||||
---
|
||||
|
||||
## Authentication and Security
|
||||
|
||||
### Core Authentication
|
||||
|
||||
| Variable | Required | Description |
|
||||
| --------------------------------------- | -------- | ------------------------------------------------------------------------- |
|
||||
| `NEXTAUTH_SECRET` | Yes | Secret for NextAuth.js session encryption. Must be at least 32 characters |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_KEY` | Yes | Primary key for encrypting sensitive data. Must be at least 32 characters |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY` | Yes | Secondary encryption key for key rotation. Must be at least 32 characters |
|
||||
|
||||
### Google OAuth
|
||||
|
||||
| Variable | Required | Description |
|
||||
| ----------------------------------- | -------- | -------------------------- |
|
||||
| `NEXT_PRIVATE_GOOGLE_CLIENT_ID` | No | Google OAuth client ID |
|
||||
| `NEXT_PRIVATE_GOOGLE_CLIENT_SECRET` | No | Google OAuth client secret |
|
||||
|
||||
Callback URL: `https://<your-domain>/api/auth/callback/google`
|
||||
|
||||
### Microsoft OAuth
|
||||
|
||||
| Variable | Required | Description |
|
||||
| -------------------------------------- | -------- | ---------------------------------------- |
|
||||
| `NEXT_PRIVATE_MICROSOFT_CLIENT_ID` | No | Microsoft/Azure AD application client ID |
|
||||
| `NEXT_PRIVATE_MICROSOFT_CLIENT_SECRET` | No | Microsoft/Azure AD client secret |
|
||||
|
||||
Callback URL: `https://<your-domain>/api/auth/callback/microsoft`
|
||||
|
||||
### Generic OIDC
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ---------------------------------- | ------- | -------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_OIDC_WELL_KNOWN` | - | OIDC provider well-known configuration URL |
|
||||
| `NEXT_PRIVATE_OIDC_CLIENT_ID` | - | OIDC client ID |
|
||||
| `NEXT_PRIVATE_OIDC_CLIENT_SECRET` | - | OIDC client secret |
|
||||
| `NEXT_PRIVATE_OIDC_PROVIDER_LABEL` | `OIDC` | Label displayed on the OIDC sign-in button |
|
||||
| `NEXT_PRIVATE_OIDC_SKIP_VERIFY` | `false` | Skip email verification for OIDC accounts |
|
||||
| `NEXT_PRIVATE_OIDC_PROMPT` | `login` | OIDC prompt parameter. Set to empty string to omit |
|
||||
|
||||
---
|
||||
|
||||
## Email Configuration
|
||||
|
||||
Documenso supports multiple email transports for sending notifications.
|
||||
|
||||
### Transport Selection
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ----------------------------- | --------------------------------------------------------------------- | ----------- |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Email transport: `smtp-auth`, `smtp-api`, `resend`, or `mailchannels` | `smtp-auth` |
|
||||
|
||||
### SMTP Authentication (smtp-auth)
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ------------------------------------- | ----------- | ---------------------------------------- |
|
||||
| `NEXT_PRIVATE_SMTP_HOST` | `127.0.0.1` | SMTP server hostname |
|
||||
| `NEXT_PRIVATE_SMTP_PORT` | `587` | SMTP server port |
|
||||
| `NEXT_PRIVATE_SMTP_USERNAME` | - | SMTP authentication username |
|
||||
| `NEXT_PRIVATE_SMTP_PASSWORD` | - | SMTP authentication password |
|
||||
| `NEXT_PRIVATE_SMTP_SECURE` | `false` | Force TLS connection (`true` or `false`) |
|
||||
| `NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS` | `false` | Disable TLS entirely (not recommended) |
|
||||
| `NEXT_PRIVATE_SMTP_SERVICE` | - | Nodemailer service name (e.g., `gmail`) |
|
||||
|
||||
### SMTP API (smtp-api)
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ------------------------------- | -------- | ------------------------------------ |
|
||||
| `NEXT_PRIVATE_SMTP_HOST` | - | SMTP server hostname |
|
||||
| `NEXT_PRIVATE_SMTP_PORT` | `587` | SMTP server port |
|
||||
| `NEXT_PRIVATE_SMTP_APIKEY_USER` | `apikey` | API key user for SMTP authentication |
|
||||
| `NEXT_PRIVATE_SMTP_APIKEY` | - | API key for SMTP authentication |
|
||||
|
||||
### Resend
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------------- | ----------------------- |
|
||||
| `NEXT_PRIVATE_RESEND_API_KEY` | API key from Resend.com |
|
||||
|
||||
### MailChannels
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------------------------- | --------------------------------- | ----------------------------------------- |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_API_KEY` | MailChannels API key | - |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_ENDPOINT` | Custom API endpoint (for proxies) | `https://api.mailchannels.net/tx/v1/send` |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN` | Domain for DKIM signing | - |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR` | DKIM selector | - |
|
||||
| `NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY` | DKIM private key | - |
|
||||
|
||||
### Sender Configuration
|
||||
|
||||
| Variable | Description |
|
||||
| -------------------------------- | ------------------------------- |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | Sender email address (required) |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_NAME` | Sender display name (required) |
|
||||
|
||||
For detailed email setup, see [Email Configuration](/docs/self-hosting/configuration/email).
|
||||
|
||||
---
|
||||
|
||||
## Storage Configuration
|
||||
|
||||
Documenso can store documents in the database or S3-compatible storage.
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ---------------------------------------- | ------------------------------------------- | ---------- |
|
||||
| `NEXT_PUBLIC_UPLOAD_TRANSPORT` | Storage backend: `database` or `s3` | `database` |
|
||||
| `NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT` | Maximum upload size displayed to users (MB) | `5` |
|
||||
|
||||
### S3 Configuration
|
||||
|
||||
Required when `NEXT_PUBLIC_UPLOAD_TRANSPORT` is set to `s3`:
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --------------------------------------- | ---------------------------------------------- | ----------- |
|
||||
| `NEXT_PRIVATE_UPLOAD_BUCKET` | S3 bucket name | |
|
||||
| `NEXT_PRIVATE_UPLOAD_REGION` | S3 region | `us-east-1` |
|
||||
| `NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID` | S3 access key ID | |
|
||||
| `NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY` | S3 secret access key | |
|
||||
| `NEXT_PRIVATE_UPLOAD_ENDPOINT` | Custom S3 endpoint for S3-compatible providers | |
|
||||
| `NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE` | Use path-style URLs instead of virtual hosts | `false` |
|
||||
|
||||
### CloudFront Distribution (Optional)
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------------------------------- | ------------------------------- |
|
||||
| `NEXT_PRIVATE_UPLOAD_DISTRIBUTION_DOMAIN` | CloudFront distribution domain |
|
||||
| `NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_ID` | CloudFront key pair ID |
|
||||
| `NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_CONTENTS` | CloudFront private key contents |
|
||||
|
||||
For detailed storage setup, see [Storage Configuration](/docs/self-hosting/configuration/storage).
|
||||
|
||||
---
|
||||
|
||||
## Signing Certificate Configuration
|
||||
|
||||
Documenso requires a certificate to digitally sign documents.
|
||||
|
||||
### Transport Selection
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------------- | ---------------------------------------- | ------- |
|
||||
| `NEXT_PRIVATE_SIGNING_TRANSPORT` | Signing backend: `local` or `gcloud-hsm` | `local` |
|
||||
|
||||
### Local Signing
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------------ | -------------------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` | Path to the `.p12` certificate file |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` | Base64-encoded `.p12` file contents (alternative to file path) |
|
||||
| `NEXT_PRIVATE_SIGNING_PASSPHRASE` | Passphrase for the certificate |
|
||||
|
||||
### Google Cloud HSM
|
||||
|
||||
| Variable | Description |
|
||||
| -------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH` | Google Cloud HSM key path |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH` | Path to the public certificate file |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS` | Base64-encoded public certificate |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS` | Base64-encoded Google Cloud credentials |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH` | Path to the certificate chain file |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS` | Base64-encoded certificate chain |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH` | Google Secret Manager path for certificate retrieval |
|
||||
|
||||
### Signature Options
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------------- | ----------------------------------------------------------- | ---------- |
|
||||
| `NEXT_PRIVATE_SIGNING_TIMESTAMP_AUTHORITY` | Comma-separated timestamp authority URLs for LTV signatures | |
|
||||
| `NEXT_PUBLIC_SIGNING_CONTACT_INFO` | Contact info embedded in PDF signatures | Webapp URL |
|
||||
| `NEXT_PRIVATE_USE_LEGACY_SIGNING_SUBFILTER` | Use `adbe.pkcs7.detached` instead of `ETSI.CAdES.detached` | `false` |
|
||||
|
||||
For detailed certificate setup, see [Signing Certificate](/docs/self-hosting/configuration/signing-certificate).
|
||||
|
||||
---
|
||||
|
||||
## Feature Flags
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------- | ----------------------------------------------- | ------- |
|
||||
| `NEXT_PUBLIC_DISABLE_SIGNUP` | Disable public user registration | `false` |
|
||||
| `NEXT_PUBLIC_POSTHOG_KEY` | PostHog API key for analytics and feature flags | |
|
||||
| `NEXT_PUBLIC_FEATURE_BILLING_ENABLED` | Enable billing features | `false` |
|
||||
|
||||
---
|
||||
|
||||
## AI Features
|
||||
|
||||
Documenso can use Google Vertex AI for recipient and field detection.
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------- | ---------------------------------------------- | -------- |
|
||||
| `GOOGLE_VERTEX_PROJECT_ID` | Google Cloud project ID with Vertex AI enabled | |
|
||||
| `GOOGLE_VERTEX_API_KEY` | Vertex AI Express API key | |
|
||||
| `GOOGLE_VERTEX_LOCATION` | Vertex AI region | `global` |
|
||||
|
||||
AI features must also be enabled in organisation/team settings after configuration.
|
||||
|
||||
---
|
||||
|
||||
## Background Jobs
|
||||
|
||||
Documenso uses a PostgreSQL-based job queue by default. Jobs (email delivery, document processing, webhook dispatch) are stored in the `BackgroundJob` table and processed via internal HTTP requests. No external queue service like Redis is required.
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------ | ------- |
|
||||
| `NEXT_PRIVATE_JOBS_PROVIDER` | Jobs provider: `local` (PostgreSQL-based queue) or `inngest` (managed service) | `local` |
|
||||
|
||||
### Inngest Configuration
|
||||
|
||||
| Variable | Description |
|
||||
| -------------------------------- | -------------------------------------------- |
|
||||
| `NEXT_PRIVATE_INNGEST_EVENT_KEY` | Inngest event key |
|
||||
| `INNGEST_EVENT_KEY` | Alternative Inngest event key |
|
||||
| `INNGEST_SIGNING_KEY` | Inngest signing key for webhook verification |
|
||||
| `NEXT_PRIVATE_INNGEST_APP_ID` | Custom Inngest app ID |
|
||||
|
||||
---
|
||||
|
||||
## Telemetry
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ----------------------------- | -------------------------------------------- | ------- |
|
||||
| `DOCUMENSO_DISABLE_TELEMETRY` | Set to `true` to disable anonymous telemetry | `false` |
|
||||
|
||||
Telemetry collects only: app version, installation ID, and node ID. No personal data is collected.
|
||||
|
||||
---
|
||||
|
||||
## Debugging and Logging
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------------ | -------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_LOGGER_FILE_PATH` | File path for log output. Disables stdout when set |
|
||||
| `NEXT_PRIVATE_BROWSERLESS_URL` | Browserless.io URL for PDF generation |
|
||||
| `NEXT_PUBLIC_USE_INTERNAL_URL_BROWSERLESS` | Use internal webapp URL in browserless requests |
|
||||
|
||||
---
|
||||
|
||||
## Enterprise Features
|
||||
|
||||
These variables require an active [Enterprise Edition](/docs/policies/enterprise-edition) license. Obtain a license key from [license.documenso.com](https://license.documenso.com) and set it below to unlock enterprise features such as SSO, embed authoring, and 21 CFR Part 11 compliance.
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------ | ------------------------------------------------ |
|
||||
| `NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY` | License key for enterprise features |
|
||||
| `NEXT_PRIVATE_STRIPE_API_KEY` | Stripe API key for billing |
|
||||
| `NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET` | Stripe webhook secret |
|
||||
| `NEXT_PRIVATE_SES_ACCESS_KEY_ID` | AWS SES access key for email domain verification |
|
||||
| `NEXT_PRIVATE_SES_SECRET_ACCESS_KEY` | AWS SES secret key |
|
||||
| `NEXT_PRIVATE_SES_REGION` | AWS SES region |
|
||||
|
||||
---
|
||||
|
||||
## Example .env File
|
||||
|
||||
A minimal production configuration:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
NEXTAUTH_SECRET="your-random-secret-at-least-32-chars"
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY="your-encryption-key-at-least-32-chars"
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY="your-secondary-key-at-least-32-chars"
|
||||
NEXT_PUBLIC_WEBAPP_URL="https://sign.example.com"
|
||||
|
||||
# Database
|
||||
NEXT_PRIVATE_DATABASE_URL="postgres://user:password@localhost:5432/documenso"
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL="postgres://user:password@localhost:5432/documenso"
|
||||
|
||||
# Email
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||
NEXT_PRIVATE_SMTP_HOST="smtp.example.com"
|
||||
NEXT_PRIVATE_SMTP_PORT=587
|
||||
NEXT_PRIVATE_SMTP_USERNAME="your-smtp-user"
|
||||
NEXT_PRIVATE_SMTP_PASSWORD="your-smtp-password"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@example.com"
|
||||
|
||||
# Signing (certificate must be configured)
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE="your-certificate-password"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Database Configuration](/docs/self-hosting/configuration/database) - Set up PostgreSQL
|
||||
- [Email Configuration](/docs/self-hosting/configuration/email) - Configure email delivery
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - Set up S3 storage
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Configure document signing
|
||||
- [Troubleshooting](/docs/self-hosting/maintenance/troubleshooting) - Common configuration issues
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Configure your self-hosted Documenso instance with environment variables, database, email, storage, and signing.
|
||||
---
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Environment Variables"
|
||||
description="Complete reference of all configuration options."
|
||||
href="/docs/self-hosting/configuration/environment"
|
||||
/>
|
||||
<Card
|
||||
title="Database"
|
||||
description="PostgreSQL setup and configuration."
|
||||
href="/docs/self-hosting/configuration/database"
|
||||
/>
|
||||
<Card
|
||||
title="Email"
|
||||
description="SMTP and email delivery configuration."
|
||||
href="/docs/self-hosting/configuration/email"
|
||||
/>
|
||||
<Card
|
||||
title="Storage"
|
||||
description="File storage with S3 or compatible services."
|
||||
href="/docs/self-hosting/configuration/storage"
|
||||
/>
|
||||
<Card
|
||||
title="Signing Certificate"
|
||||
description="Digital signature certificate setup."
|
||||
href="/docs/self-hosting/configuration/signing-certificate"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## Required Configuration
|
||||
|
||||
At minimum, you need to configure:
|
||||
|
||||
1. **Database** - PostgreSQL connection
|
||||
2. **Email** - SMTP for sending notifications
|
||||
3. **Base URL** - Your instance's public URL
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Quick Start](/docs/self-hosting/getting-started/quick-start) - Get running first, configure later
|
||||
- [Troubleshooting](/docs/self-hosting/maintenance/troubleshooting) - Common configuration issues
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "Configuration",
|
||||
"pages": [
|
||||
"environment",
|
||||
"database",
|
||||
"email",
|
||||
"storage",
|
||||
"signing-certificate",
|
||||
"telemetry",
|
||||
"advanced"
|
||||
]
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Google Cloud HSM
|
||||
description: Configure Google Cloud HSM for hardware-based signing key protection.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
Google Cloud HSM provides hardware-based key protection. The private key never leaves the HSM.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Enable Cloud KMS
|
||||
|
||||
Create or use a Google Cloud project and enable the Cloud KMS API.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create HSM key
|
||||
|
||||
Create an HSM key ring and an asymmetric signing key in Cloud KMS (see [Creating an HSM Key](#creating-an-hsm-key) below).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create service account
|
||||
|
||||
Create a service account with the `cloudkms.signerVerifier` role so Documenso can use the key.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Export public certificate
|
||||
|
||||
Export the public certificate from the HSM key; Documenso needs it to embed in signatures.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_SIGNING_TRANSPORT` | Set to `gcloud-hsm` |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH` | Full resource path to the HSM key version |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH` | Path to the public certificate file |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS` | Base64-encoded public certificate |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS` | Base64-encoded Google Cloud service account JSON |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH` | Path to the certificate chain file |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS` | Base64-encoded certificate chain |
|
||||
| `NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH` | Google Secret Manager path for certificate retrieval |
|
||||
|
||||
## Configuration Example
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SIGNING_TRANSPORT=gcloud-hsm
|
||||
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH=projects/my-project/locations/global/keyRings/documenso/cryptoKeys/signing-key/cryptoKeyVersions/1
|
||||
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH=/opt/documenso/public.crt
|
||||
NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS=eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii...
|
||||
```
|
||||
|
||||
## Key Path Format
|
||||
|
||||
The HSM key path follows this format:
|
||||
|
||||
```
|
||||
projects/{project}/locations/{location}/keyRings/{keyring}/cryptoKeys/{key}/cryptoKeyVersions/{version}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
projects/my-company/locations/us-east1/keyRings/document-signing/cryptoKeys/prod-signing/cryptoKeyVersions/1
|
||||
```
|
||||
|
||||
## Creating an HSM Key
|
||||
|
||||
Using gcloud CLI:
|
||||
|
||||
```bash
|
||||
# Create a key ring
|
||||
gcloud kms keyrings create document-signing \
|
||||
--location=us-east1 \
|
||||
--project=my-project
|
||||
|
||||
# Create an asymmetric signing key
|
||||
gcloud kms keys create prod-signing \
|
||||
--keyring=document-signing \
|
||||
--location=us-east1 \
|
||||
--purpose=asymmetric-signing \
|
||||
--default-algorithm=rsa-sign-pkcs1-2048-sha256 \
|
||||
--protection-level=hsm \
|
||||
--project=my-project
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Signing Certificate
|
||||
description: Configure the X.509 signing certificate used for digital signatures in self-hosted Documenso.
|
||||
---
|
||||
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
When a document is completed in Documenso, it is digitally signed using an X.509 certificate. This cryptographic signature provides:
|
||||
|
||||
- Proof of document authenticity
|
||||
- Verification that the document has not been modified since signing
|
||||
- Identity information about the signing entity
|
||||
|
||||
Self-hosted Documenso instances require a signing certificate. You can generate a self-signed certificate or purchase one from a Certificate Authority (CA).
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Local Certificate"
|
||||
description="Configure a local .p12 certificate file or base64-encoded contents."
|
||||
href="/docs/self-hosting/configuration/signing-certificate/local"
|
||||
/>
|
||||
<Card
|
||||
title="Google Cloud HSM"
|
||||
description="Hardware-based key protection with Google Cloud KMS."
|
||||
href="/docs/self-hosting/configuration/signing-certificate/google-cloud-hsm"
|
||||
/>
|
||||
<Card
|
||||
title="Timestamp Server"
|
||||
description="Add trusted timestamps and customise signature appearance."
|
||||
href="/docs/self-hosting/configuration/signing-certificate/timestamp-server"
|
||||
/>
|
||||
<Card
|
||||
title="Troubleshooting"
|
||||
description="Common certificate errors and solutions."
|
||||
href="/docs/self-hosting/configuration/signing-certificate/troubleshooting"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## Certificate Options
|
||||
|
||||
<Tabs items={['Self-Signed', 'CA-Issued', 'Google Cloud HSM']}>
|
||||
<Tab value="Self-Signed">
|
||||
|
||||
A self-signed certificate is sufficient for most use cases where your industry has no special signing regulations.
|
||||
|
||||
**Advantages:**
|
||||
|
||||
- Free to create
|
||||
- Full control over certificate details
|
||||
- Works for internal and business documents
|
||||
|
||||
**Limitations:**
|
||||
|
||||
- PDF readers like Adobe Acrobat will not show a green checkmark
|
||||
- Not recognized by Adobe's trust list
|
||||
- Recipients see a warning that the signature cannot be verified
|
||||
|
||||
The certificate still includes your organisation details and guarantees document integrity.
|
||||
|
||||
</Tab>
|
||||
<Tab value="CA-Issued">
|
||||
|
||||
Purchase a certificate from a Certificate Authority if you need:
|
||||
|
||||
- Green checkmark in Adobe PDF readers
|
||||
- Industry-specific compliance requirements
|
||||
- Third-party signature validation
|
||||
|
||||
For Adobe recognition, choose a vendor from the [Adobe Approved Trust List](https://helpx.adobe.com/acrobat/kb/approved-trust-list1.html).
|
||||
|
||||
</Tab>
|
||||
<Tab value="Google Cloud HSM">
|
||||
|
||||
For organisations requiring hardware-based key protection, Documenso supports Google Cloud HSM. This provides:
|
||||
|
||||
- FIPS 140-2 Level 3 certified key storage
|
||||
- Keys never leave the HSM
|
||||
- Audit logging of all cryptographic operations
|
||||
|
||||
See [Google Cloud HSM](/docs/self-hosting/configuration/signing-certificate/google-cloud-hsm) for setup instructions.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Local Certificate
|
||||
description: Generate or convert a .p12 certificate and configure it for document signing.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Generating a Self-Signed Certificate
|
||||
|
||||
Create a self-signed certificate using OpenSSL. These commands work on Linux, macOS, and Windows Subsystem for Linux (WSL).
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
|
||||
### Generate a private key
|
||||
|
||||
Create a 2048-bit RSA private key:
|
||||
|
||||
```bash
|
||||
openssl genrsa -out private.key 2048
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
### Create a self-signed certificate
|
||||
|
||||
Generate a certificate valid for one year:
|
||||
|
||||
```bash
|
||||
openssl req -new -x509 -key private.key -out certificate.crt -days 365
|
||||
```
|
||||
|
||||
When prompted, enter your organisation details:
|
||||
|
||||
| Field | Example |
|
||||
| ------------------- | ----------------------- |
|
||||
| Country Name | US |
|
||||
| State or Province | California |
|
||||
| Locality Name | San Francisco |
|
||||
| Organization Name | Your Company Inc |
|
||||
| Organizational Unit | Engineering |
|
||||
| Common Name | Your Company Signing CA |
|
||||
| Email Address | admin@example.com |
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
### Create the .p12 certificate
|
||||
|
||||
Combine the private key and certificate into a PKCS#12 (.p12) file:
|
||||
|
||||
```bash
|
||||
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
You must set a password when prompted. Certificates without passwords cause signing failures with
|
||||
the error "Failed to get private key bags".
|
||||
</Callout>
|
||||
|
||||
To set the password non-interactively:
|
||||
|
||||
```bash
|
||||
# Set password securely (won't appear in command history)
|
||||
read -s -p "Enter certificate password: " CERT_PASS
|
||||
echo
|
||||
|
||||
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt \
|
||||
-password env:CERT_PASS
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
### Clean up
|
||||
|
||||
Remove the intermediate files:
|
||||
|
||||
```bash
|
||||
rm private.key certificate.crt
|
||||
```
|
||||
|
||||
Keep `certificate.p12` and the password secure.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Using an Existing Certificate
|
||||
|
||||
If you have an existing certificate from a CA, convert it to PKCS#12 format.
|
||||
|
||||
<Tabs items={['From PEM files', 'From other formats']}>
|
||||
<Tab value="From PEM files">
|
||||
|
||||
If you have separate certificate and key files:
|
||||
|
||||
```bash
|
||||
openssl pkcs12 -export -out certificate.p12 \
|
||||
-inkey private.key \
|
||||
-in certificate.crt \
|
||||
-certfile chain.crt
|
||||
```
|
||||
|
||||
Include `-certfile chain.crt` if you have intermediate certificates.
|
||||
|
||||
</Tab>
|
||||
<Tab value="From other formats">
|
||||
|
||||
**Convert from DER to PEM first:**
|
||||
|
||||
```bash
|
||||
openssl x509 -inform DER -in certificate.der -out certificate.crt
|
||||
openssl rsa -inform DER -in private.der -out private.key
|
||||
```
|
||||
|
||||
**PFX files** are the same format as P12. You can use them directly by renaming to `.p12`.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Certificate Validation
|
||||
|
||||
Verify your certificate is valid:
|
||||
|
||||
```bash
|
||||
# Check certificate details
|
||||
openssl pkcs12 -in certificate.p12 -info -nokeys
|
||||
|
||||
# Verify password works
|
||||
openssl pkcs12 -in certificate.p12 -noout
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------------ | -------------------------------------------------------------- |
|
||||
| `NEXT_PRIVATE_SIGNING_TRANSPORT` | Set to `local` (default) |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` | Path to the `.p12` certificate file |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` | Base64-encoded `.p12` file contents (alternative to file path) |
|
||||
| `NEXT_PRIVATE_SIGNING_PASSPHRASE` | Passphrase for the certificate |
|
||||
|
||||
## Using File Path
|
||||
|
||||
Mount the certificate file and set the path:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SIGNING_TRANSPORT=local
|
||||
NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE=your-certificate-password
|
||||
```
|
||||
|
||||
**Docker example:**
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-v /path/to/certificate.p12:/opt/documenso/cert.p12:ro \
|
||||
-e NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12 \
|
||||
-e NEXT_PRIVATE_SIGNING_PASSPHRASE="your-certificate-password" \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
## Using Base64-Encoded Contents
|
||||
|
||||
For environments where file mounting is not available (e.g., Railway, Vercel):
|
||||
|
||||
```bash
|
||||
# Encode the certificate
|
||||
base64 -i certificate.p12
|
||||
```
|
||||
|
||||
Set the encoded string as an environment variable:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SIGNING_TRANSPORT=local
|
||||
NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS=MIIKEQIBAzCCCdcGCSqGSIb3DQEHAaCCCcg...
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE=your-certificate-password
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
The `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` variable takes precedence over
|
||||
`NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` if both are set.
|
||||
</Callout>
|
||||
|
||||
## Docker File Permissions
|
||||
|
||||
When running in Docker, the application runs as user 1001. The certificate file must be readable:
|
||||
|
||||
```bash
|
||||
# On the host, before mounting
|
||||
sudo chown 1001 certificate.p12
|
||||
chmod 400 certificate.p12
|
||||
```
|
||||
|
||||
Or mount as read-only and ensure group readability:
|
||||
|
||||
```bash
|
||||
chmod 440 certificate.p12
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Signing Certificate",
|
||||
"pages": ["...index", "local", "google-cloud-hsm", "timestamp-server", "troubleshooting"]
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Timestamp Server
|
||||
description: Configure timestamp servers and signature appearance for document signing.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
Timestamp servers add a trusted timestamp to signatures, proving when the document was signed. This enables Long-Term Validation (LTV) and archival timestamps.
|
||||
|
||||
## Environment Variable
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------------ | ------------------------------------------------ |
|
||||
| `NEXT_PRIVATE_SIGNING_TIMESTAMP_AUTHORITY` | Comma-separated list of timestamp authority URLs |
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SIGNING_TIMESTAMP_AUTHORITY=http://timestamp.digicert.com,http://timestamp.globalsign.com/tsa/r6advanced1
|
||||
```
|
||||
|
||||
## Free Timestamp Servers
|
||||
|
||||
| Provider | URL |
|
||||
| ---------- | ------------------------------------------------- |
|
||||
| DigiCert | `http://timestamp.digicert.com` |
|
||||
| GlobalSign | `http://timestamp.globalsign.com/tsa/r6advanced1` |
|
||||
| FreeTSA | `https://freetsa.org/tsr` |
|
||||
| Sectigo | `http://timestamp.sectigo.com` |
|
||||
|
||||
<Callout type="info">
|
||||
Timestamp servers add latency to the signing process. Each server in the list is tried in order
|
||||
until one succeeds.
|
||||
</Callout>
|
||||
|
||||
## Benefits of Timestamping
|
||||
|
||||
- Proves when the document was signed
|
||||
- Signature remains valid after certificate expiration
|
||||
- Required for some compliance standards
|
||||
- Enables long-term archival
|
||||
|
||||
## Signature Appearance
|
||||
|
||||
### Contact Information
|
||||
|
||||
Set contact information embedded in PDF signatures:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_SIGNING_CONTACT_INFO=support@example.com
|
||||
```
|
||||
|
||||
If not set, defaults to your `NEXT_PUBLIC_WEBAPP_URL`.
|
||||
|
||||
### Legacy Signature Format
|
||||
|
||||
For compatibility with older PDF readers, use the legacy signature subfilter:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_USE_LEGACY_SIGNING_SUBFILTER=true
|
||||
```
|
||||
|
||||
| Subfilter | Description | Default |
|
||||
| --------------------- | ----------------------------- | ------- |
|
||||
| `ETSI.CAdES.detached` | Modern CAdES-based signatures | Yes |
|
||||
| `adbe.pkcs7.detached` | Legacy PKCS#7 signatures | No |
|
||||
|
||||
Use the legacy format only if recipients report compatibility issues with older software.
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Troubleshooting
|
||||
description: Common signing certificate errors and their solutions.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Failed to get private key bags">
|
||||
Cause:
|
||||
- Certificate has no password set
|
||||
|
||||
Solution:
|
||||
- Verify the certificate has a password set
|
||||
- Re-create with a password:
|
||||
|
||||
```bash
|
||||
openssl pkcs12 -export -out new-certificate.p12 -inkey private.key -in certificate.crt
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="No certificate found for local signing">
|
||||
Cause:
|
||||
- Certificate path or contents not configured
|
||||
|
||||
Solution:
|
||||
- Verify that `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` or `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` is set
|
||||
- Check the file exists at the configured path
|
||||
- Verify file permissions allow reading
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Permission denied reading certificate">
|
||||
Cause:
|
||||
- Application cannot read the certificate file
|
||||
|
||||
Solution:
|
||||
- For Docker, run `sudo chown 1001 certificate.p12` and `chmod 400 certificate.p12` on the host before mounting
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Invalid password (mac verify failure)">
|
||||
Cause:
|
||||
- Incorrect passphrase
|
||||
|
||||
Solution:
|
||||
- Verify `NEXT_PRIVATE_SIGNING_PASSPHRASE` matches the certificate password
|
||||
- Check for trailing whitespace
|
||||
- Test with `openssl pkcs12 -in certificate.p12 -noout`
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Certificate has expired">
|
||||
Cause:
|
||||
- Certificate validity period has ended
|
||||
|
||||
Solution:
|
||||
- Generate a new certificate or obtain a renewed one
|
||||
- Update the certificate file and restart Documenso
|
||||
- Check expiration:
|
||||
|
||||
```bash
|
||||
openssl pkcs12 -in certificate.p12 -nokeys | openssl x509 -noout -dates
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Base64 decode error">
|
||||
Cause:
|
||||
- Incorrect base64 encoding or line breaks
|
||||
|
||||
Solution:
|
||||
- Encode without line breaks:
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
base64 -i certificate.p12 | tr -d '\n'
|
||||
|
||||
# Linux
|
||||
base64 -w 0 certificate.p12
|
||||
```
|
||||
- Ensure the entire base64 string is on one line
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Google Cloud HSM authentication failed">
|
||||
Cause:
|
||||
- Google Cloud credentials not configured or invalid
|
||||
|
||||
Solution:
|
||||
- Verify that `NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS` is set
|
||||
- Check the service account has `cloudkms.signerVerifier` role
|
||||
- Verify the base64 encoding of the service account JSON
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
@@ -0,0 +1,569 @@
|
||||
---
|
||||
title: Storage Configuration
|
||||
description: Configure file storage for uploaded documents and signed PDFs using database storage (default) or S3-compatible object storage.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Storage Options
|
||||
|
||||
| Backend | Best For | Scalability | Configuration |
|
||||
| ---------- | -------------------------------- | ----------- | ------------- |
|
||||
| `database` | Small deployments, simplicity | Limited | None required |
|
||||
| `s3` | Production, large files, backups | High | Required |
|
||||
|
||||
Select the storage backend with the `NEXT_PUBLIC_UPLOAD_TRANSPORT` environment variable:
|
||||
|
||||
```bash
|
||||
# Database storage (default)
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=database
|
||||
|
||||
# S3-compatible storage
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Storage
|
||||
|
||||
Database storage is the default option and requires no additional configuration. Documents are stored as base64-encoded data directly in PostgreSQL.
|
||||
|
||||
<Tabs items={['Advantages', 'Limitations']}>
|
||||
<Tab value="Advantages">
|
||||
|
||||
- No external dependencies
|
||||
- Simple deployment
|
||||
- Automatic backups with database
|
||||
|
||||
</Tab>
|
||||
<Tab value="Limitations">
|
||||
|
||||
- Increases database size significantly
|
||||
- Slower for large files
|
||||
- Database backup/restore takes longer
|
||||
- Not recommended for files larger than 10MB
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Configuration
|
||||
|
||||
No configuration required. Database storage is enabled when `NEXT_PUBLIC_UPLOAD_TRANSPORT` is unset or set to `database`.
|
||||
|
||||
---
|
||||
|
||||
## S3 Configuration
|
||||
|
||||
S3 storage is recommended for production deployments. Documenso supports AWS S3 and any S3-compatible storage service.
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description |
|
||||
| --------------------------------------- | --------------------------------- |
|
||||
| `NEXT_PUBLIC_UPLOAD_TRANSPORT` | Set to `s3` |
|
||||
| `NEXT_PRIVATE_UPLOAD_BUCKET` | S3 bucket name |
|
||||
| `NEXT_PRIVATE_UPLOAD_REGION` | AWS region (default: `us-east-1`) |
|
||||
| `NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID` | AWS access key ID |
|
||||
| `NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY` | AWS secret access key |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------------------- | --------------------------------------------- | ----------- |
|
||||
| `NEXT_PRIVATE_UPLOAD_ENDPOINT` | Custom S3 endpoint for S3-compatible services | |
|
||||
| `NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE` | Use path-style URLs instead of virtual-hosted | `false` |
|
||||
| `NEXT_PRIVATE_UPLOAD_REGION` | S3 region | `us-east-1` |
|
||||
|
||||
---
|
||||
|
||||
## AWS S3 Setup
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Create an S3 Bucket
|
||||
|
||||
Create a bucket in the AWS Console or using the CLI:
|
||||
|
||||
```bash
|
||||
aws s3 mb s3://your-documenso-bucket --region us-east-1
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure Bucket Policy
|
||||
|
||||
Block public access and configure CORS for presigned URL uploads:
|
||||
|
||||
**CORS Configuration:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"AllowedHeaders": ["*"],
|
||||
"AllowedMethods": ["GET", "PUT", "POST"],
|
||||
"AllowedOrigins": ["https://your-documenso-domain.com"],
|
||||
"ExposeHeaders": ["ETag"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Apply via AWS Console (Bucket > Permissions > CORS configuration) or CLI:
|
||||
|
||||
```bash
|
||||
aws s3api put-bucket-cors --bucket your-documenso-bucket --cors-configuration file://cors.json
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create IAM User
|
||||
|
||||
Create an IAM user with programmatic access and attach this policy:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
|
||||
"Resource": "arn:aws:s3:::your-documenso-bucket/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure Environment Variables
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=your-documenso-bucket
|
||||
NEXT_PRIVATE_UPLOAD_REGION=us-east-1
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## MinIO Setup
|
||||
|
||||
MinIO is a self-hosted S3-compatible object storage server.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Deploy MinIO
|
||||
|
||||
Using Docker:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name minio \
|
||||
-p 9000:9000 \
|
||||
-p 9001:9001 \
|
||||
-e MINIO_ROOT_USER=minioadmin \
|
||||
-e MINIO_ROOT_PASSWORD=minioadmin \
|
||||
-v minio_data:/data \
|
||||
minio/minio server /data --console-address ":9001"
|
||||
```
|
||||
|
||||
Using Docker Compose with Documenso:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
environment:
|
||||
MINIO_ROOT_USER: minioadmin
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
|
||||
volumes:
|
||||
minio_data:
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create a Bucket
|
||||
|
||||
Access the MinIO Console at `http://localhost:9001` and create a bucket, or use the CLI:
|
||||
|
||||
```bash
|
||||
# Install MinIO client
|
||||
mc alias set myminio http://localhost:9000 minioadmin minioadmin
|
||||
|
||||
# Create bucket
|
||||
mc mb myminio/documenso
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure Environment Variables
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT=http://minio:9000
|
||||
NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE=true
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=minioadmin
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=minioadmin
|
||||
NEXT_PRIVATE_UPLOAD_REGION=us-east-1
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="info">
|
||||
Set `NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE=true` for MinIO and other S3-compatible services that
|
||||
don't support virtual-hosted bucket URLs.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## Other S3-Compatible Services
|
||||
|
||||
Documenso works with any S3-compatible storage service. Configure the endpoint and enable path-style URLs if required.
|
||||
|
||||
### Cloudflare R2
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=your-r2-access-key
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=your-r2-secret-key
|
||||
NEXT_PRIVATE_UPLOAD_REGION=auto
|
||||
```
|
||||
|
||||
### DigitalOcean Spaces
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT=https://nyc3.digitaloceanspaces.com
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=your-spaces-key
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=your-spaces-secret
|
||||
NEXT_PRIVATE_UPLOAD_REGION=nyc3
|
||||
```
|
||||
|
||||
### Backblaze B2
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT=https://s3.us-west-004.backblazeb2.com
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=your-b2-key-id
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=your-b2-application-key
|
||||
NEXT_PRIVATE_UPLOAD_REGION=us-west-004
|
||||
```
|
||||
|
||||
### Wasabi
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT=https://s3.us-east-1.wasabisys.com
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=your-wasabi-key
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=your-wasabi-secret
|
||||
NEXT_PRIVATE_UPLOAD_REGION=us-east-1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CloudFront CDN (Optional)
|
||||
|
||||
Use Amazon CloudFront to serve documents with lower latency and reduced S3 costs. CloudFront integration uses signed URLs for secure access.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- An S3 bucket configured for Documenso
|
||||
- A CloudFront distribution with the S3 bucket as origin
|
||||
- A CloudFront key pair for signing URLs
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Create a CloudFront Distribution
|
||||
|
||||
- Go to CloudFront in the AWS Console
|
||||
- Create a distribution with your S3 bucket as the origin
|
||||
- Configure Origin Access Control (OAC) to restrict direct S3 access
|
||||
- Set the default cache behavior to allow GET requests
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Create a Key Pair
|
||||
|
||||
CloudFront signed URLs require a key pair:
|
||||
|
||||
- Go to CloudFront > Key management > Public keys
|
||||
- Create a new public key
|
||||
- Create a key group containing the public key
|
||||
- Associate the key group with your distribution
|
||||
|
||||
Keep the private key secure - you'll need it for the environment variable.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure Environment Variables
|
||||
|
||||
```bash
|
||||
# CloudFront distribution domain (without https://)
|
||||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_DOMAIN=d1234567890.cloudfront.net
|
||||
|
||||
# CloudFront key pair ID
|
||||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_ID=K1234567890ABC
|
||||
|
||||
# Private key contents (PEM format)
|
||||
NEXT_PRIVATE_UPLOAD_DISTRIBUTION_KEY_CONTENTS="-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA...
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
Store the private key securely. Use environment variables or secrets management rather than
|
||||
committing it to version control.
|
||||
</Callout>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### How It Works
|
||||
|
||||
When CloudFront is configured:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Uploads
|
||||
|
||||
File uploads still go directly to S3 via presigned URLs.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Downloads
|
||||
|
||||
File downloads use CloudFront signed URLs.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Caching
|
||||
|
||||
CloudFront caches files at edge locations.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Expiration
|
||||
|
||||
Signed URLs expire after 1 hour.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Migration Between Storage Backends
|
||||
|
||||
Documenso does not provide automatic migration between storage backends. Each document's storage location is recorded in the database.
|
||||
|
||||
<Callout type="warn">
|
||||
Documents uploaded to one storage backend cannot be automatically migrated to another. Plan your
|
||||
storage strategy before deploying to production.
|
||||
</Callout>
|
||||
|
||||
### Manual Migration Process
|
||||
|
||||
To migrate existing documents from database to S3 storage:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Export documents
|
||||
|
||||
Extract document blobs from the database (e.g. via a script querying `DocumentData` where `type` is `BYTES_64`).
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Upload to S3
|
||||
|
||||
Upload each exported file to your S3 bucket and note the resulting object keys or paths.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Update DocumentData records
|
||||
|
||||
Point each record to the new S3 location by updating `DocumentData` with the S3 path and setting `type` to `S3_PATH`.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
This requires custom scripts and database modifications.
|
||||
|
||||
<Callout type="info">
|
||||
For production deployments, we recommend starting with S3 storage from the beginning.
|
||||
</Callout>
|
||||
|
||||
### Hybrid Operation
|
||||
|
||||
During migration, Documenso can read from both backends. The `DocumentData.type` field indicates where each document is stored.
|
||||
|
||||
- `BYTES_64`: Stored in database
|
||||
- `S3_PATH`: Stored in S3
|
||||
|
||||
New uploads use the configured `NEXT_PUBLIC_UPLOAD_TRANSPORT` backend.
|
||||
|
||||
---
|
||||
|
||||
## Storage Sizing
|
||||
|
||||
### Database Storage Estimates
|
||||
|
||||
When using database storage, plan for significant database growth:
|
||||
|
||||
| Documents/Month | Avg Size | Monthly Growth | Annual Growth |
|
||||
| --------------- | -------- | -------------- | ------------- |
|
||||
| 100 | 500KB | ~50MB | ~600MB |
|
||||
| 1,000 | 500KB | ~500MB | ~6GB |
|
||||
| 10,000 | 500KB | ~5GB | ~60GB |
|
||||
|
||||
Database storage includes base64 encoding overhead (~33% increase).
|
||||
|
||||
### S3 Storage Estimates
|
||||
|
||||
S3 stores files without encoding overhead:
|
||||
|
||||
| Documents/Month | Avg Size | Monthly Growth | Annual Growth |
|
||||
| --------------- | -------- | -------------- | ------------- |
|
||||
| 100 | 500KB | ~50MB | ~600MB |
|
||||
| 1,000 | 500KB | ~500MB | ~6GB |
|
||||
| 10,000 | 500KB | ~5GB | ~60GB |
|
||||
|
||||
### Cost Comparison
|
||||
|
||||
For high-volume deployments, S3 is more cost-effective:
|
||||
|
||||
| Aspect | Database Storage | S3 Storage |
|
||||
| ------------- | ---------------------------- | ----------------------- |
|
||||
| Storage cost | Database pricing (~$0.10/GB) | S3 pricing (~$0.023/GB) |
|
||||
| Transfer cost | Database I/O | S3 requests + egress |
|
||||
| Backup cost | Larger database backups | Separate S3 backups |
|
||||
| Performance | Degrades with size | Consistent |
|
||||
|
||||
---
|
||||
|
||||
## Upload Size Limits
|
||||
|
||||
Configure the maximum upload size displayed to users:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT=10
|
||||
```
|
||||
|
||||
This value is in megabytes. The default is 5MB.
|
||||
|
||||
<Callout type="info">
|
||||
This environment variable controls the UI display. Actual limits may also be enforced by your
|
||||
reverse proxy, web server, or S3 configuration.
|
||||
</Callout>
|
||||
|
||||
Ensure your infrastructure supports the configured limit:
|
||||
|
||||
<Tabs items={['Nginx', 'S3', 'CloudFront']}>
|
||||
<Tab value="Nginx">
|
||||
|
||||
Set `client_max_body_size` to match or exceed your upload limit.
|
||||
|
||||
</Tab>
|
||||
<Tab value="S3">
|
||||
|
||||
Default object size limit is 5GB; multipart upload may be required for large files.
|
||||
|
||||
</Tab>
|
||||
<Tab value="CloudFront">
|
||||
|
||||
Default limit is 50MB per request.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Access Denied when uploading">
|
||||
Causes:
|
||||
- Incorrect IAM credentials
|
||||
- Bucket policy doesn't allow required operations
|
||||
- CORS not configured for presigned URL uploads
|
||||
|
||||
Solutions:
|
||||
- Verify IAM user has `s3:PutObject` permission
|
||||
- Check bucket policy allows writes
|
||||
- Configure CORS to allow your domain
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Invalid endpoint (getaddrinfo ENOTFOUND)">
|
||||
Causes:
|
||||
- Incorrect region configuration
|
||||
- Bucket doesn't exist
|
||||
- Network connectivity issues
|
||||
|
||||
Solutions:
|
||||
- Verify the bucket exists in the specified region
|
||||
- Check the endpoint URL is correct
|
||||
- Verify network access to S3
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="SignatureDoesNotMatch">
|
||||
Causes:
|
||||
- Incorrect secret access key
|
||||
- Clock skew between server and S3
|
||||
|
||||
Solutions:
|
||||
- Verify credentials are correct and not expired
|
||||
- Ensure server time is synchronized (use NTP)
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Path-style access required">
|
||||
Set path-style access for S3-compatible services:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE=true
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="CloudFront signed URL errors (Missing required key 'Key')">
|
||||
Causes:
|
||||
- Key pair not configured correctly
|
||||
- Private key format invalid
|
||||
|
||||
Solutions:
|
||||
- Verify the key pair ID matches the CloudFront configuration
|
||||
- Ensure the private key is in PEM format
|
||||
- Check for whitespace or encoding issues in the key contents
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Database Configuration](/docs/self-hosting/configuration/database) - Configure PostgreSQL
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Complete configuration reference
|
||||
- [Backups](/docs/self-hosting/maintenance/backups) - Backup strategies for both storage backends
|
||||
- [Docker Compose](/docs/self-hosting/deployment/docker-compose) - Deploy with MinIO for local storage
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: Telemetry
|
||||
description: Learn about the telemetry data that Documenso collects from self-hosted instances.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
## Overview
|
||||
|
||||
Documenso collects anonymous telemetry data from self-hosted instances to help us understand how the software is being used and make improvements to the product. This telemetry is enabled by default, but you can easily disable it if you prefer.
|
||||
|
||||
## What We Collect
|
||||
|
||||
We collect minimal, privacy-preserving information that helps us understand the health and adoption of self-hosted installations:
|
||||
|
||||
- **App Version**: The version of Documenso you are running. This helps us understand which versions are in use and prioritize support for older versions.
|
||||
|
||||
- **Installation ID**: A unique identifier for your installation. This is stored in your database and helps us count distinct installations without knowing who you are.
|
||||
|
||||
- **Node ID**: A unique identifier for each server or container instance. This is stored in your operating system's temporary directory and helps us understand deployment patterns (for example, how many instances are running in a cluster).
|
||||
|
||||
### What We Don't Collect
|
||||
|
||||
We do **not** collect any of the following:
|
||||
|
||||
- Personal information about you or your users
|
||||
- Document contents or file names
|
||||
- User email addresses or names
|
||||
- Usage patterns or feature usage statistics
|
||||
- Server logs or error messages
|
||||
- Any data that could identify your organisation or users
|
||||
|
||||
## Why We Collect Telemetry
|
||||
|
||||
The telemetry data we collect serves several important purposes:
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Product Improvement">
|
||||
Understanding which versions are in use helps us prioritize bug fixes and security updates for
|
||||
the versions that matter most.
|
||||
</Accordion>
|
||||
<Accordion title="Support Planning">
|
||||
Knowing how many installations exist and their deployment patterns helps us plan support
|
||||
resources and documentation.
|
||||
</Accordion>
|
||||
<Accordion title="Feature Development">
|
||||
Understanding deployment patterns (like cluster sizes) helps us make better architectural
|
||||
decisions for future features.
|
||||
</Accordion>
|
||||
<Accordion title="Community Health">
|
||||
Tracking adoption helps us understand the growth of the self-hosted community and allocate
|
||||
resources accordingly.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
All of this is done anonymously and in aggregate. We cannot identify you, your organisation, or your users from the telemetry data we collect.
|
||||
|
||||
## Events We Track
|
||||
|
||||
We track two simple events:
|
||||
|
||||
<Tabs items={['Server Startup', 'Server Heartbeat']}>
|
||||
<Tab value="Server Startup">
|
||||
|
||||
Captured once when your server starts. This tells us when installations are first set up or restarted.
|
||||
|
||||
</Tab>
|
||||
<Tab value="Server Heartbeat">
|
||||
|
||||
Captured every hour while your server is running. This helps us understand how many active installations exist and their uptime patterns.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## How to Disable Telemetry
|
||||
|
||||
If you prefer not to send telemetry data, you can disable it by setting an environment variable.
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
Add the following to your environment configuration:
|
||||
|
||||
```bash
|
||||
DOCUMENSO_DISABLE_TELEMETRY=true
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
If you're using Docker, you can set this in your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- DOCUMENSO_DISABLE_TELEMETRY=true
|
||||
```
|
||||
|
||||
Or pass it when running a container:
|
||||
|
||||
```bash
|
||||
docker run -e DOCUMENSO_DISABLE_TELEMETRY=true ...
|
||||
```
|
||||
|
||||
### After Disabling
|
||||
|
||||
Once you set `DOCUMENSO_DISABLE_TELEMETRY=true` and restart your server, no telemetry data will be sent. The telemetry client will not initialize, and no network requests will be made to our telemetry servers.
|
||||
|
||||
<Callout type="info">
|
||||
If you previously had telemetry enabled, the installation ID stored in your database will remain,
|
||||
but it will no longer be used or sent anywhere.
|
||||
</Callout>
|
||||
|
||||
## Questions or Concerns
|
||||
|
||||
If you have questions about our telemetry practices or concerns about privacy, please reach out to us. We're committed to transparency and respect your choice to disable telemetry if you prefer.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - All configuration options
|
||||
- [Quick Start](/docs/self-hosting/getting-started/quick-start) - Get started with self-hosting
|
||||
@@ -0,0 +1,483 @@
|
||||
---
|
||||
title: Docker Compose
|
||||
description: Deploy Documenso with Docker Compose, including PostgreSQL. This production-ready setup is suitable for most self-hosted deployments.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/) 20.10 or later
|
||||
- [Docker Compose](https://docs.docker.com/compose/install/) v2.0 or later
|
||||
- SMTP credentials for sending emails
|
||||
- At least 2GB of available RAM
|
||||
- A domain name (for production deployments)
|
||||
|
||||
Verify your installation:
|
||||
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
## Clone and Configure
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Download the compose file
|
||||
|
||||
Download the production Docker Compose file:
|
||||
|
||||
```bash
|
||||
mkdir documenso && cd documenso
|
||||
curl -O https://raw.githubusercontent.com/documenso/documenso/release/docker/production/compose.yml
|
||||
```
|
||||
|
||||
Alternatively, clone the full repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/documenso/documenso.git
|
||||
cd documenso/docker/production
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Create environment file
|
||||
|
||||
Create a `.env` file in the same directory as `compose.yml`:
|
||||
|
||||
```bash
|
||||
touch .env
|
||||
```
|
||||
|
||||
Add the required configuration (see [Environment Configuration](#environment-configuration) below).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="warn">
|
||||
The `compose.yml` in the repository may be outdated. Use it as a starting point and verify the
|
||||
configuration against the environment variables documented here.
|
||||
</Callout>
|
||||
|
||||
## Docker Compose File Overview
|
||||
|
||||
The production `compose.yml` includes two services:
|
||||
|
||||
```yaml
|
||||
name: documenso-production
|
||||
|
||||
services:
|
||||
database:
|
||||
image: postgres:15
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER:?err}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?err}
|
||||
- POSTGRES_DB=${POSTGRES_DB:?err}
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER}']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- database:/var/lib/postgresql/data
|
||||
|
||||
documenso:
|
||||
image: documenso/documenso:latest
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# See environment configuration below
|
||||
ports:
|
||||
- ${PORT:-3000}:${PORT:-3000}
|
||||
volumes:
|
||||
- /opt/documenso/cert.p12:/opt/documenso/cert.p12:ro
|
||||
|
||||
volumes:
|
||||
database:
|
||||
```
|
||||
|
||||
| Service | Purpose |
|
||||
| ----------- | ------------------------------------------------------------ |
|
||||
| `database` | PostgreSQL 15 database with persistent storage |
|
||||
| `documenso` | Main application container, waits for database to be healthy |
|
||||
|
||||
The Documenso container waits for the database health check to pass before starting.
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Create a `.env` file with the following variables:
|
||||
|
||||
### Required Variables
|
||||
|
||||
```bash
|
||||
# Database (used by both database and documenso services)
|
||||
POSTGRES_USER=documenso
|
||||
POSTGRES_PASSWORD=your-secure-database-password
|
||||
POSTGRES_DB=documenso
|
||||
|
||||
# Application secrets (generate with: openssl rand -base64 32)
|
||||
NEXTAUTH_SECRET=your-nextauth-secret
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY=your-encryption-key-min-32-characters
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=your-secondary-key-min-32-characters
|
||||
|
||||
# Public URL where Documenso is accessible
|
||||
NEXT_PUBLIC_WEBAPP_URL=https://sign.example.com
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL=http://localhost:3000
|
||||
|
||||
# Database connection (uses Docker service name)
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://documenso:your-secure-database-password@database:5432/documenso
|
||||
|
||||
# Email configuration
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT=smtp-auth
|
||||
NEXT_PRIVATE_SMTP_HOST=smtp.example.com
|
||||
NEXT_PRIVATE_SMTP_PORT=587
|
||||
NEXT_PRIVATE_SMTP_USERNAME=your-smtp-username
|
||||
NEXT_PRIVATE_SMTP_PASSWORD=your-smtp-password
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME=Documenso
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS=noreply@example.com
|
||||
```
|
||||
|
||||
### Optional Variables
|
||||
|
||||
```bash
|
||||
# Application port (default: 3000)
|
||||
PORT=3000
|
||||
|
||||
# Signing certificate (see Signing Certificate section)
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE=your-certificate-password
|
||||
|
||||
# Disable public signups
|
||||
NEXT_PUBLIC_DISABLE_SIGNUP=false
|
||||
```
|
||||
|
||||
<Callout type="info">Generate secure secrets using: `openssl rand -base64 32`</Callout>
|
||||
|
||||
For the complete list of environment variables, see [Environment Variables](/docs/self-hosting/configuration/environment).
|
||||
|
||||
### Generating Secrets
|
||||
|
||||
Generate the required secrets:
|
||||
|
||||
```bash
|
||||
# Generate NEXTAUTH_SECRET
|
||||
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)"
|
||||
|
||||
# Generate encryption keys
|
||||
echo "NEXT_PRIVATE_ENCRYPTION_KEY=$(openssl rand -base64 32)"
|
||||
echo "NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=$(openssl rand -base64 32)"
|
||||
|
||||
# Generate database password
|
||||
echo "POSTGRES_PASSWORD=$(openssl rand -base64 24)"
|
||||
```
|
||||
|
||||
## Signing Certificate
|
||||
|
||||
A signing certificate is required for document signing. Generate a `.p12` certificate on your host machine and mount it into the container. See [Local Certificate](/docs/self-hosting/configuration/signing-certificate/local) for how to generate one.
|
||||
|
||||
Place the certificate on the host and set permissions so the container can read it (UID 1001):
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/documenso
|
||||
sudo cp /path/to/your/cert.p12 /opt/documenso/cert.p12
|
||||
sudo chown 1001:1001 /opt/documenso/cert.p12
|
||||
sudo chmod 400 /opt/documenso/cert.p12
|
||||
```
|
||||
|
||||
The `compose.yml` mounts this path into the container. Add the passphrase to your `.env` file:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE=your-certificate-password
|
||||
```
|
||||
|
||||
If file mounting is not available, you can set `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` with the base64-encoded certificate string instead.
|
||||
|
||||
For production deployments that require Adobe Approved Trust List recognition, consider using a [Google Cloud HSM](/docs/self-hosting/configuration/signing-certificate/google-cloud-hsm) or another external HSM.
|
||||
|
||||
<Callout type="warn">
|
||||
Do not generate or store the signing certificate inside the container. If the container is
|
||||
destroyed and rebuilt, or if you run multiple instances, the certificate will be lost or
|
||||
inconsistent. Always provide the certificate externally.
|
||||
</Callout>
|
||||
|
||||
## Starting Services
|
||||
|
||||
Start all services:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env up -d
|
||||
```
|
||||
|
||||
Check that containers are running:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```
|
||||
NAME STATUS PORTS
|
||||
documenso-production-database-1 running (healthy) 5432/tcp
|
||||
documenso-production-documenso-1 running 0.0.0.0:3000->3000/tcp
|
||||
```
|
||||
|
||||
Wait for the database to be healthy and for migrations to complete. Check the logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -f documenso
|
||||
```
|
||||
|
||||
Look for "Ready" or "Listening on port 3000" in the output.
|
||||
|
||||
## Accessing Documenso
|
||||
|
||||
Once the containers are running:
|
||||
|
||||
- **Local access**: Open [http://localhost:3000](http://localhost:3000)
|
||||
- **Remote access**: Use the URL configured in `NEXT_PUBLIC_WEBAPP_URL`
|
||||
|
||||
### First Account Setup
|
||||
|
||||
Navigate to the signup page and create your account. Verify your email address — if emails are not being delivered, check the container logs for SMTP errors.
|
||||
|
||||
<Callout type="info">
|
||||
All accounts created through signup are regular user accounts. Admin access must be granted
|
||||
directly in the database. Once your accounts are set up, consider disabling public signups by
|
||||
setting `NEXT_PUBLIC_DISABLE_SIGNUP=true`.
|
||||
</Callout>
|
||||
|
||||
## Managing Services
|
||||
|
||||
### View Logs
|
||||
|
||||
View all service logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
View logs for a specific service:
|
||||
|
||||
```bash
|
||||
docker compose logs -f documenso
|
||||
docker compose logs -f database
|
||||
```
|
||||
|
||||
### Restart Services
|
||||
|
||||
Restart all services:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env restart
|
||||
```
|
||||
|
||||
Restart a specific service:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env restart documenso
|
||||
```
|
||||
|
||||
### Stop Services
|
||||
|
||||
Stop without removing containers:
|
||||
|
||||
```bash
|
||||
docker compose stop
|
||||
```
|
||||
|
||||
Stop and remove containers (preserves volumes):
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
Stop, remove containers, and delete data:
|
||||
|
||||
```bash
|
||||
docker compose down -v
|
||||
```
|
||||
|
||||
<Callout type="error">
|
||||
Using `down -v` deletes the database volume. Back up your data first.
|
||||
</Callout>
|
||||
|
||||
### Update Documenso
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Pull the latest image:
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
Recreate containers:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env up -d
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Database migrations run automatically on container startup.
|
||||
|
||||
<Callout type="warn">
|
||||
Back up your database before upgrading. See [Backups](/docs/self-hosting/maintenance/backups).
|
||||
</Callout>
|
||||
|
||||
## Production Considerations
|
||||
|
||||
### Reverse Proxy
|
||||
|
||||
For production, place Documenso behind a reverse proxy for SSL termination:
|
||||
|
||||
**nginx example:**
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name sign.example.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Caddy example:**
|
||||
|
||||
```
|
||||
sign.example.com {
|
||||
reverse_proxy localhost:3000
|
||||
}
|
||||
```
|
||||
|
||||
### Database Backups
|
||||
|
||||
Set up automated database backups:
|
||||
|
||||
```bash
|
||||
# Manual backup
|
||||
docker compose exec database pg_dump -U documenso documenso > backup.sql
|
||||
|
||||
# Restore from backup
|
||||
docker compose exec -T database psql -U documenso documenso < backup.sql
|
||||
```
|
||||
|
||||
See [Backups](/docs/self-hosting/maintenance/backups) for automated backup strategies.
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Add resource limits to prevent container resource exhaustion:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
documenso:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
### External Database
|
||||
|
||||
For production, consider using a managed PostgreSQL service:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
Remove the `database` service from `compose.yml`
|
||||
</Step>
|
||||
<Step>
|
||||
Update environment variables:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://user:password@your-db-host:5432/documenso
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://user:password@your-db-host:5432/documenso
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### S3 Storage
|
||||
|
||||
For high-volume deployments, configure S3-compatible storage:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET=your-bucket
|
||||
NEXT_PRIVATE_UPLOAD_REGION=us-east-1
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=your-access-key
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=your-secret-key
|
||||
```
|
||||
|
||||
See [Storage Configuration](/docs/self-hosting/configuration/storage).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Container fails to start">
|
||||
Check logs: `docker compose logs documenso`. Common causes: missing environment variables
|
||||
(ensure all required variables are in `.env`), database not ready (the container waits for
|
||||
database health check), port conflict (change `PORT` in `.env` if 3000 is in use).
|
||||
</Accordion>
|
||||
<Accordion title="Database connection errors">
|
||||
Verify the database is healthy: `docker compose ps database`. Test: `docker compose exec
|
||||
database psql -U documenso -d documenso -c "SELECT 1"`.
|
||||
</Accordion>
|
||||
<Accordion title="Certificate errors">
|
||||
Check certificate status: `curl http://localhost:3000/api/certificate-status`. Common issues:
|
||||
file not found (verify volume mount in `compose.yml`), permission denied (run `chown 1001:1001`
|
||||
on the certificate file), no password (the certificate must have a password).
|
||||
</Accordion>
|
||||
<Accordion title="Emails not sending">
|
||||
Check `NEXT_PRIVATE_SMTP_TRANSPORT` matches your setup, verify host, port, username, and
|
||||
password, check logs: `docker compose logs documenso | grep -i smtp`.
|
||||
</Accordion>
|
||||
<Accordion title="Application unreachable">
|
||||
Verify containers are running: `docker compose ps`; check the port mapping matches your `.env`;
|
||||
test locally: `curl http://localhost:3000/api/health`; check firewall rules allow traffic on the
|
||||
configured port.
|
||||
</Accordion>
|
||||
<Accordion title="Out of disk space">
|
||||
Check disk usage: `docker system df`. Clean up: `docker system prune -a`.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Full configuration reference
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Certificate setup details
|
||||
- [Email Configuration](/docs/self-hosting/configuration/email) - SMTP and email provider setup
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - S3 storage setup
|
||||
- [Backups](/docs/self-hosting/maintenance/backups) - Backup strategies
|
||||
- [Upgrades](/docs/self-hosting/maintenance/upgrades) - Upgrade procedures
|
||||
@@ -0,0 +1,307 @@
|
||||
---
|
||||
title: Docker
|
||||
description: Run Documenso as a standalone Docker container with an external database. Ideal for container platforms like AWS ECS, Google Cloud Run, or Azure Container Instances.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
## Prerequisites
|
||||
|
||||
For a self-contained setup that includes PostgreSQL, see [Docker Compose Deployment](/docs/self-hosting/deployment/docker-compose).
|
||||
|
||||
Before starting, ensure you have:
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/) 20.10 or later installed
|
||||
- A PostgreSQL 14+ database accessible from your Docker host
|
||||
- SMTP credentials for sending emails
|
||||
- At least 1GB of available RAM
|
||||
|
||||
Verify Docker is installed:
|
||||
|
||||
```bash
|
||||
docker --version
|
||||
```
|
||||
|
||||
## Pulling the Docker Image
|
||||
|
||||
```bash
|
||||
docker pull documenso/documenso:latest
|
||||
```
|
||||
|
||||
### Available Tags
|
||||
|
||||
| Tag | Description |
|
||||
| --------- | -------------------------------- |
|
||||
| `latest` | Latest stable release |
|
||||
| `x.y.z` | Specific version (e.g., `1.5.0`) |
|
||||
| `release` | Latest release branch build |
|
||||
|
||||
<Callout type="info">
|
||||
Pin to a specific version tag in production to avoid unexpected updates.
|
||||
</Callout>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Required Variables
|
||||
|
||||
These variables must be set for Documenso to start:
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --------------------------------------- | --------------------------------------------------------------------------- | ----------------------------- |
|
||||
| `NEXTAUTH_SECRET` | Secret key for session encryption. Generate with `openssl rand -base64 32` | - |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_KEY` | Primary encryption key (minimum 32 characters) | - |
|
||||
| `NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY` | Secondary encryption key (minimum 32 characters) | - |
|
||||
| `NEXT_PUBLIC_WEBAPP_URL` | Public URL where Documenso is accessible (e.g., `https://sign.example.com`) | - |
|
||||
| `NEXT_PRIVATE_INTERNAL_WEBAPP_URL` | Internal URL the app uses to call itself (for background jobs) | Same as `NEXT_PUBLIC_WEBAPP_URL` |
|
||||
| `NEXT_PRIVATE_DATABASE_URL` | PostgreSQL connection string | - |
|
||||
| `NEXT_PRIVATE_SMTP_TRANSPORT` | Email transport: `smtp-auth`, `smtp-api`, `resend`, or `mailchannels` | `smtp-auth` |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_NAME` | Sender name for outgoing emails | - |
|
||||
| `NEXT_PRIVATE_SMTP_FROM_ADDRESS` | Sender email address | - |
|
||||
|
||||
### Database Connection
|
||||
|
||||
Documenso requires two database connection strings:
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
| ---------------------------------- | ------------ | --------------------------- | --------------------------------- |
|
||||
| `NEXT_PRIVATE_DATABASE_URL` | Yes | - | Pooled connection for app queries |
|
||||
| `NEXT_PRIVATE_DIRECT_DATABASE_URL` | When pooling | `NEXT_PRIVATE_DATABASE_URL` | Direct connection for migrations |
|
||||
|
||||
Connection string format:
|
||||
|
||||
```
|
||||
postgresql://user:password@host:5432/database
|
||||
```
|
||||
|
||||
If you're not using a connection pooler like PgBouncer, both variables can use the same connection string.
|
||||
|
||||
### SMTP Configuration
|
||||
|
||||
For `smtp-auth` transport:
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
| ---------------------------- | -------- | ----------- | ------------------------------------- |
|
||||
| `NEXT_PRIVATE_SMTP_HOST` | Yes | `127.0.0.1` | SMTP server hostname |
|
||||
| `NEXT_PRIVATE_SMTP_PORT` | Yes | `587` | SMTP server port (usually 587 or 465) |
|
||||
| `NEXT_PRIVATE_SMTP_USERNAME` | Yes | - | SMTP username |
|
||||
| `NEXT_PRIVATE_SMTP_PASSWORD` | Yes | - | SMTP password |
|
||||
| `NEXT_PRIVATE_SMTP_SECURE` | No | `false` | Set to `true` to force TLS |
|
||||
|
||||
See [Email Configuration](/docs/self-hosting/configuration/email) for other transport options.
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------------- | -------------------------------------------------------------- | ------------------------- |
|
||||
| `PORT` | Port the application listens on | `3000` |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` | Path to signing certificate inside container | `/opt/documenso/cert.p12` |
|
||||
| `NEXT_PRIVATE_SIGNING_PASSPHRASE` | Passphrase for the signing certificate | - |
|
||||
| `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` | Base64-encoded `.p12` certificate (alternative to file path) | - |
|
||||
| `NEXT_PUBLIC_UPLOAD_TRANSPORT` | Document storage: `database` or `s3` | `database` |
|
||||
| `NEXT_PUBLIC_DISABLE_SIGNUP` | Disable public signups | `false` |
|
||||
|
||||
For the complete list, see [Environment Variables](/docs/self-hosting/configuration/environment).
|
||||
|
||||
## Running with Docker
|
||||
|
||||
### Minimum Example
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name documenso \
|
||||
-p 3000:3000 \
|
||||
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
||||
-e NEXTAUTH_SECRET="$(openssl rand -base64 32)" \
|
||||
-e NEXT_PRIVATE_ENCRYPTION_KEY="your-encryption-key-min-32-chars" \
|
||||
-e NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY="your-secondary-key-min-32-chars" \
|
||||
-e NEXT_PUBLIC_WEBAPP_URL="https://sign.example.com" \
|
||||
-e NEXT_PRIVATE_INTERNAL_WEBAPP_URL="http://localhost:3000" \
|
||||
-e NEXT_PRIVATE_DATABASE_URL="postgresql://user:password@db-host:5432/documenso" \
|
||||
-e NEXT_PRIVATE_SIGNING_PASSPHRASE="your-certificate-password" \
|
||||
-e NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth" \
|
||||
-e NEXT_PRIVATE_SMTP_HOST="smtp.example.com" \
|
||||
-e NEXT_PRIVATE_SMTP_PORT="587" \
|
||||
-e NEXT_PRIVATE_SMTP_USERNAME="your-smtp-user" \
|
||||
-e NEXT_PRIVATE_SMTP_PASSWORD="your-smtp-password" \
|
||||
-e NEXT_PRIVATE_SMTP_FROM_NAME="Documenso" \
|
||||
-e NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@example.com" \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
### Signing Certificate
|
||||
|
||||
A signing certificate is required for document signing. You have two options for providing one:
|
||||
|
||||
- **Volume mount** — mount a `.p12` file from the host into the container at `/opt/documenso/cert.p12` (shown above). This is the simplest approach for small to moderate deployments.
|
||||
- **Base64-encoded contents** — set `NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS` with the base64-encoded certificate string. Use this when file mounting is not available (e.g., Railway, Vercel).
|
||||
|
||||
For production deployments that require Adobe Approved Trust List recognition, consider using a [Google Cloud HSM](/docs/self-hosting/configuration/signing-certificate/google-cloud-hsm) or another external HSM.
|
||||
|
||||
<Callout type="warn">
|
||||
Do not generate or store the signing certificate inside the container. If the container is
|
||||
destroyed and rebuilt, or if you run multiple instances, the certificate will be lost or
|
||||
inconsistent. Always provide the certificate externally.
|
||||
</Callout>
|
||||
|
||||
See [Signing Certificate Configuration](/docs/self-hosting/configuration/signing-certificate) for generating certificates and detailed setup.
|
||||
|
||||
### Using an Environment File
|
||||
|
||||
For easier management, use an environment file:
|
||||
|
||||
Create `.env`:
|
||||
|
||||
```bash
|
||||
NEXTAUTH_SECRET=your-secret-here
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY=your-encryption-key-min-32-chars
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=your-secondary-key-min-32-chars
|
||||
NEXT_PUBLIC_WEBAPP_URL=https://sign.example.com
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL=http://localhost:3000
|
||||
NEXT_PRIVATE_DATABASE_URL=postgresql://user:password@db-host:5432/documenso
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL=postgresql://user:password@db-host:5432/documenso
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE=your-certificate-password
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT=smtp-auth
|
||||
NEXT_PRIVATE_SMTP_HOST=smtp.example.com
|
||||
NEXT_PRIVATE_SMTP_PORT=587
|
||||
NEXT_PRIVATE_SMTP_USERNAME=your-smtp-user
|
||||
NEXT_PRIVATE_SMTP_PASSWORD=your-smtp-password
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME=Documenso
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS=noreply@example.com
|
||||
```
|
||||
|
||||
Run with the environment file:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name documenso \
|
||||
-p 3000:3000 \
|
||||
--env-file .env \
|
||||
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
Documenso provides health check endpoints for monitoring:
|
||||
|
||||
| Endpoint | Purpose |
|
||||
| ------------------------- | -------------------------------------------------------------- |
|
||||
| `/api/health` | Checks database connectivity and certificate status |
|
||||
| `/api/certificate-status` | Returns whether a signing certificate is configured and usable |
|
||||
|
||||
### Docker Health Check
|
||||
|
||||
Add a health check to your container:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name documenso \
|
||||
-p 3000:3000 \
|
||||
--health-cmd="curl -f http://localhost:3000/api/health || exit 1" \
|
||||
--health-interval=30s \
|
||||
--health-timeout=10s \
|
||||
--health-retries=3 \
|
||||
--env-file .env \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
Check container health status:
|
||||
|
||||
```bash
|
||||
docker inspect --format='{{.State.Health.Status}}' documenso
|
||||
```
|
||||
|
||||
## Updating the Container
|
||||
|
||||
To update Documenso to a new version:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
|
||||
### Pull the new image
|
||||
|
||||
```bash
|
||||
docker pull documenso/documenso:latest
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
### Stop and remove the existing container
|
||||
|
||||
```bash
|
||||
docker stop documenso
|
||||
docker rm documenso
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
### Start a new container with the same configuration
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name documenso \
|
||||
-p 3000:3000 \
|
||||
--env-file .env \
|
||||
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Callout type="info">
|
||||
Database migrations run automatically when the container starts. Back up your database before
|
||||
upgrading.
|
||||
</Callout>
|
||||
|
||||
## Persistence
|
||||
|
||||
The Documenso container is stateless. All persistent data is stored in the PostgreSQL database.
|
||||
|
||||
By default, documents are also stored in the database. For high-volume deployments, configure S3-compatible storage instead. See [Storage Configuration](/docs/self-hosting/configuration/storage) for S3 setup.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Container fails to start">
|
||||
Check the container logs: `docker logs documenso`. Common causes: missing required environment
|
||||
variables (ensure all required variables are set), database connection failed (verify the
|
||||
database URL and network connectivity), port already in use (change the host port or stop the
|
||||
conflicting service).
|
||||
</Accordion>
|
||||
<Accordion title="Database connection errors">
|
||||
Test connectivity: `docker run --rm postgres:15 psql
|
||||
"postgresql://user:password@host:5432/database" -c "SELECT 1"`. If using Docker networks, ensure
|
||||
the container can reach the database.
|
||||
</Accordion>
|
||||
<Accordion title="Certificate errors">
|
||||
Check certificate status: `curl http://localhost:3000/api/certificate-status`. Common issues:
|
||||
file not found (verify the volume mount path), permission denied (ensure the file is readable,
|
||||
UID 1001 inside the container), no password (the certificate must have a password set). Fix
|
||||
permissions: `chmod 644 /path/to/cert.p12` and `chown 1001:1001 /path/to/cert.p12`.
|
||||
</Accordion>
|
||||
<Accordion title="Emails not sending">
|
||||
Verify SMTP configuration: check `NEXT_PRIVATE_SMTP_TRANSPORT` matches your configuration, for
|
||||
`smtp-auth` ensure host, port, username, and password are correct, check if your SMTP provider
|
||||
requires TLS (`NEXT_PRIVATE_SMTP_SECURE=true`).
|
||||
</Accordion>
|
||||
<Accordion title="Container runs but application is inaccessible">
|
||||
Verify port mapping `-p 3000:3000`, check if `NEXT_PUBLIC_WEBAPP_URL` matches how you're
|
||||
accessing the application, ensure no firewall is blocking the port.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Docker Compose Deployment](/docs/self-hosting/deployment/docker-compose) - Multi-container setup with PostgreSQL included
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Full configuration reference
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Set up document signing
|
||||
- [Email Configuration](/docs/self-hosting/configuration/email) - Configure SMTP providers
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - Set up S3-compatible storage
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Deployment
|
||||
description: Deploy Documenso using Docker, Docker Compose, Railway, or Kubernetes.
|
||||
---
|
||||
|
||||
| Method | Best For | Difficulty |
|
||||
| -------------------------------------------------------------- | ----------------------------------- | ---------- |
|
||||
| [Docker](/docs/self-hosting/deployment/docker) | Single container, external database | Easy |
|
||||
| [Docker Compose](/docs/self-hosting/deployment/docker-compose) | Self-contained setup, small teams | Easy |
|
||||
| [Railway](/docs/self-hosting/deployment/railway) | Quick deployment, managed | Very Easy |
|
||||
| [Kubernetes](/docs/self-hosting/deployment/kubernetes) | Enterprise, high availability | Advanced |
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Docker"
|
||||
description="Single container with external database."
|
||||
href="/docs/self-hosting/deployment/docker"
|
||||
/>
|
||||
<Card
|
||||
title="Docker Compose"
|
||||
description="Production-ready multi-container setup."
|
||||
href="/docs/self-hosting/deployment/docker-compose"
|
||||
/>
|
||||
<Card
|
||||
title="Railway"
|
||||
description="One-click deployment on Railway."
|
||||
href="/docs/self-hosting/deployment/railway"
|
||||
/>
|
||||
<Card
|
||||
title="Kubernetes"
|
||||
description="Scalable Kubernetes deployment."
|
||||
href="/docs/self-hosting/deployment/kubernetes"
|
||||
/>
|
||||
</Cards>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Requirements](/docs/self-hosting/getting-started/requirements) - Check prerequisites first
|
||||
- [Configuration](/docs/self-hosting/configuration) - Configure your deployment
|
||||
@@ -0,0 +1,868 @@
|
||||
---
|
||||
title: Kubernetes
|
||||
description: Deploy Documenso on Kubernetes for production environments requiring high availability, auto-scaling, and enterprise-grade infrastructure.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
|
||||
- A Kubernetes cluster (1.24+) running and accessible
|
||||
- `kubectl` configured to communicate with your cluster
|
||||
- A PostgreSQL 14+ database (in-cluster or external)
|
||||
- SMTP credentials for sending emails
|
||||
- A domain name with DNS configured
|
||||
- An Ingress controller installed (nginx-ingress, traefik, etc.)
|
||||
|
||||
Verify your cluster connection:
|
||||
|
||||
```bash
|
||||
kubectl cluster-info
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
A typical Documenso Kubernetes deployment consists of:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Ingress (TLS termination)"] --> B["Service (ClusterIP)"]
|
||||
B --> C1["Pod (app)"]
|
||||
B --> C2["Pod (app)"]
|
||||
B --> C3["Pod (app)"]
|
||||
C1 --> D["PostgreSQL (external or in-cluster)"]
|
||||
C2 --> D
|
||||
C3 --> D
|
||||
```
|
||||
|
||||
## Namespace Setup
|
||||
|
||||
Create a dedicated namespace for Documenso:
|
||||
|
||||
```yaml
|
||||
# namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
```
|
||||
|
||||
Apply the namespace:
|
||||
|
||||
```bash
|
||||
kubectl apply -f namespace.yaml
|
||||
```
|
||||
|
||||
## Secrets Configuration
|
||||
|
||||
Store sensitive configuration in Kubernetes Secrets.
|
||||
|
||||
### Generate Secrets
|
||||
|
||||
First, generate the required secret values:
|
||||
|
||||
```bash
|
||||
# Generate NEXTAUTH_SECRET
|
||||
openssl rand -base64 32
|
||||
|
||||
# Generate encryption keys (minimum 32 characters each)
|
||||
openssl rand -base64 32
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
### Create the Secret
|
||||
|
||||
```yaml
|
||||
# secret.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: documenso-secrets
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
type: Opaque
|
||||
stringData:
|
||||
NEXTAUTH_SECRET: 'your-generated-secret-here'
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY: 'your-encryption-key-min-32-chars'
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: 'your-secondary-key-min-32-chars'
|
||||
NEXT_PRIVATE_DATABASE_URL: 'postgresql://user:password@postgres-host:5432/documenso'
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL: 'postgresql://user:password@postgres-host:5432/documenso'
|
||||
NEXT_PRIVATE_SMTP_USERNAME: 'your-smtp-username'
|
||||
NEXT_PRIVATE_SMTP_PASSWORD: 'your-smtp-password'
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE: 'your-certificate-passphrase'
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
Never commit Secret manifests with real values to version control. Use a secrets management tool
|
||||
like Sealed Secrets, External Secrets Operator, or your cloud provider's secret manager.
|
||||
</Callout>
|
||||
|
||||
Apply the secret:
|
||||
|
||||
```bash
|
||||
kubectl apply -f secret.yaml
|
||||
```
|
||||
|
||||
## ConfigMap
|
||||
|
||||
Store non-sensitive configuration in a ConfigMap:
|
||||
|
||||
```yaml
|
||||
# configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: documenso-config
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
data:
|
||||
NEXT_PUBLIC_WEBAPP_URL: 'https://sign.example.com'
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT: 'smtp-auth'
|
||||
NEXT_PRIVATE_SMTP_HOST: 'smtp.example.com'
|
||||
NEXT_PRIVATE_SMTP_PORT: '587'
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME: 'Documenso'
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS: 'noreply@example.com'
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL: 'http://localhost:3000'
|
||||
NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH: '/opt/documenso/cert.p12'
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT: 'database'
|
||||
```
|
||||
|
||||
Apply the ConfigMap:
|
||||
|
||||
```bash
|
||||
kubectl apply -f configmap.yaml
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Create the Documenso Deployment:
|
||||
|
||||
```yaml
|
||||
# deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
containers:
|
||||
- name: documenso
|
||||
image: documenso/documenso:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: documenso-config
|
||||
- secretRef:
|
||||
name: documenso-secrets
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 30
|
||||
volumeMounts:
|
||||
- name: signing-cert
|
||||
mountPath: /opt/documenso/cert.p12
|
||||
subPath: cert.p12
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: signing-cert
|
||||
secret:
|
||||
secretName: documenso-signing-cert
|
||||
items:
|
||||
- key: cert.p12
|
||||
path: cert.p12
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
Pin to a specific image tag (e.g., `documenso/documenso:1.5.0`) in production instead of `latest`
|
||||
to ensure predictable deployments.
|
||||
</Callout>
|
||||
|
||||
### Signing Certificate Secret
|
||||
|
||||
Create a secret for the signing certificate:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic documenso-signing-cert \
|
||||
--namespace documenso \
|
||||
--from-file=cert.p12=/path/to/your/cert.p12
|
||||
```
|
||||
|
||||
Apply the deployment:
|
||||
|
||||
```bash
|
||||
kubectl apply -f deployment.yaml
|
||||
```
|
||||
|
||||
## Service
|
||||
|
||||
Expose the Deployment with a Service:
|
||||
|
||||
```yaml
|
||||
# service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
selector:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
```
|
||||
|
||||
Apply the service:
|
||||
|
||||
```bash
|
||||
kubectl apply -f service.yaml
|
||||
```
|
||||
|
||||
## Ingress Configuration
|
||||
|
||||
### nginx-ingress
|
||||
|
||||
```yaml
|
||||
# ingress-nginx.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: '50m'
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: '300'
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: '300'
|
||||
cert-manager.io/cluster-issuer: 'letsencrypt-prod'
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- sign.example.com
|
||||
secretName: documenso-tls
|
||||
rules:
|
||||
- host: sign.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: documenso
|
||||
port:
|
||||
name: http
|
||||
```
|
||||
|
||||
### Traefik
|
||||
|
||||
```yaml
|
||||
# ingress-traefik.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: 'true'
|
||||
cert-manager.io/cluster-issuer: 'letsencrypt-prod'
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- sign.example.com
|
||||
secretName: documenso-tls
|
||||
rules:
|
||||
- host: sign.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: documenso
|
||||
port:
|
||||
name: http
|
||||
```
|
||||
|
||||
Apply your chosen ingress:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ingress-nginx.yaml
|
||||
# or
|
||||
kubectl apply -f ingress-traefik.yaml
|
||||
```
|
||||
|
||||
## Database Options
|
||||
|
||||
### External Database (Recommended)
|
||||
|
||||
For production, use a managed PostgreSQL service:
|
||||
|
||||
- AWS RDS for PostgreSQL
|
||||
- Google Cloud SQL
|
||||
- Azure Database for PostgreSQL
|
||||
- DigitalOcean Managed Databases
|
||||
|
||||
Update `NEXT_PRIVATE_DATABASE_URL` in your Secret with the connection string from your provider.
|
||||
|
||||
### In-Cluster PostgreSQL
|
||||
|
||||
For testing or development, deploy PostgreSQL in the cluster:
|
||||
|
||||
```yaml
|
||||
# postgres.yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-pvc
|
||||
namespace: documenso
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgres-secrets
|
||||
namespace: documenso
|
||||
type: Opaque
|
||||
stringData:
|
||||
POSTGRES_USER: documenso
|
||||
POSTGRES_PASSWORD: your-secure-password
|
||||
POSTGRES_DB: documenso
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: documenso
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:15-alpine
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: postgres-secrets
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
volumes:
|
||||
- name: postgres-data
|
||||
persistentVolumeClaim:
|
||||
claimName: postgres-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: documenso
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
selector:
|
||||
app.kubernetes.io/name: postgres
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
In-cluster PostgreSQL is not recommended for production. It lacks high availability, automated
|
||||
backups, and point-in-time recovery that managed services provide.
|
||||
</Callout>
|
||||
|
||||
Update your Documenso secret to use the in-cluster database:
|
||||
|
||||
```yaml
|
||||
NEXT_PRIVATE_DATABASE_URL: 'postgresql://documenso:your-secure-password@postgres.documenso.svc.cluster.local:5432/documenso'
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL: 'postgresql://documenso:your-secure-password@postgres.documenso.svc.cluster.local:5432/documenso'
|
||||
```
|
||||
|
||||
## Persistent Storage
|
||||
|
||||
Documenso stores documents in the database by default. For high-volume deployments, configure S3-compatible storage.
|
||||
|
||||
### S3 Configuration
|
||||
|
||||
Add these to your ConfigMap and Secret:
|
||||
|
||||
```yaml
|
||||
# In configmap.yaml, add:
|
||||
data:
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT: 's3'
|
||||
|
||||
# In secret.yaml, add:
|
||||
stringData:
|
||||
NEXT_PRIVATE_UPLOAD_ENDPOINT: 'https://s3.amazonaws.com'
|
||||
NEXT_PRIVATE_UPLOAD_REGION: 'us-east-1'
|
||||
NEXT_PRIVATE_UPLOAD_BUCKET: 'your-documenso-bucket'
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID: 'your-access-key'
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY: 'your-secret-key'
|
||||
```
|
||||
|
||||
See [Storage Configuration](/docs/self-hosting/configuration/storage) for detailed S3 setup instructions.
|
||||
|
||||
## Scaling
|
||||
|
||||
### Horizontal Pod Autoscaler (HPA)
|
||||
|
||||
Scale Documenso based on CPU and memory utilization:
|
||||
|
||||
```yaml
|
||||
# hpa.yaml
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: documenso
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 10
|
||||
periodSeconds: 60
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 0
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100
|
||||
periodSeconds: 15
|
||||
- type: Pods
|
||||
value: 4
|
||||
periodSeconds: 15
|
||||
selectPolicy: Max
|
||||
```
|
||||
|
||||
Apply the HPA:
|
||||
|
||||
```bash
|
||||
kubectl apply -f hpa.yaml
|
||||
```
|
||||
|
||||
### Pod Disruption Budget
|
||||
|
||||
Ensure availability during cluster maintenance:
|
||||
|
||||
```yaml
|
||||
# pdb.yaml
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: documenso
|
||||
app.kubernetes.io/component: web
|
||||
```
|
||||
|
||||
Apply the PDB:
|
||||
|
||||
```bash
|
||||
kubectl apply -f pdb.yaml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Pods not starting">
|
||||
Check pod status:
|
||||
|
||||
```bash
|
||||
# Get pod status
|
||||
kubectl get pods -n documenso
|
||||
|
||||
# Describe pod
|
||||
kubectl describe pod <pod-name> -n documenso
|
||||
```
|
||||
|
||||
Common causes:
|
||||
- ImagePullBackOff (check image name and registry access)
|
||||
- CrashLoopBackOff (check logs for application errors)
|
||||
- Pending (check resource requests and node capacity)
|
||||
</Accordion>
|
||||
<Accordion title="Database connection errors">
|
||||
Verify the database is accessible from the cluster:
|
||||
|
||||
```bash
|
||||
kubectl run postgres-test --rm -it --image=postgres:15-alpine -n documenso -- psql "postgresql://user:password@host:5432/database" -c "SELECT 1"
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Ingress not working">
|
||||
Check Ingress status:
|
||||
|
||||
```bash
|
||||
# Get Ingress status
|
||||
kubectl get ingress -n documenso
|
||||
|
||||
# Describe Ingress
|
||||
kubectl describe ingress documenso -n documenso
|
||||
```
|
||||
|
||||
Verify the Ingress controller is running:
|
||||
|
||||
```bash
|
||||
# Get Ingress controller status
|
||||
kubectl get pods -n ingress-nginx
|
||||
|
||||
# Get Traefik controller status
|
||||
kubectl get pods -n traefik
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Certificate errors">
|
||||
Check the signing certificate secret:
|
||||
|
||||
```bash
|
||||
# Get signing certificate secret
|
||||
kubectl get secret documenso-signing-cert -n documenso -o yaml
|
||||
```
|
||||
|
||||
Verify the certificate is mounted correctly:
|
||||
|
||||
```bash
|
||||
# Execute command in pod
|
||||
kubectl exec -it <pod-name> -n documenso -- ls -la /opt/documenso/
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Health check failures">
|
||||
Test the health endpoint:
|
||||
|
||||
```bash
|
||||
# Port forward
|
||||
kubectl port-forward svc/documenso 3000:80 -n documenso
|
||||
|
||||
# Test health endpoint
|
||||
curl http://localhost:3000/api/health
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Rolling back a deployment">
|
||||
If a deployment fails, rollback:
|
||||
```bash
|
||||
# View history
|
||||
kubectl rollout history deployment/documenso -n documenso
|
||||
|
||||
# Rollback to previous version
|
||||
kubectl rollout undo deployment/documenso -n documenso
|
||||
|
||||
# Rollback to a specific revision
|
||||
kubectl rollout undo deployment/documenso -n documenso --to-revision=2
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Complete Example
|
||||
|
||||
Here's a combined manifest for reference:
|
||||
|
||||
```yaml
|
||||
# documenso-complete.yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: documenso
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: documenso-config
|
||||
namespace: documenso
|
||||
data:
|
||||
NEXT_PUBLIC_WEBAPP_URL: 'https://sign.example.com'
|
||||
NEXT_PRIVATE_SMTP_TRANSPORT: 'smtp-auth'
|
||||
NEXT_PRIVATE_SMTP_HOST: 'smtp.example.com'
|
||||
NEXT_PRIVATE_SMTP_PORT: '587'
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME: 'Documenso'
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS: 'noreply@example.com'
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL: 'http://localhost:3000'
|
||||
NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH: '/opt/documenso/cert.p12'
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT: 'database'
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: documenso-secrets
|
||||
namespace: documenso
|
||||
type: Opaque
|
||||
stringData:
|
||||
NEXTAUTH_SECRET: 'REPLACE_WITH_GENERATED_SECRET'
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY: 'REPLACE_WITH_32_CHAR_KEY'
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: 'REPLACE_WITH_32_CHAR_KEY'
|
||||
NEXT_PRIVATE_DATABASE_URL: 'postgresql://user:password@host:5432/documenso'
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL: 'postgresql://user:password@host:5432/documenso'
|
||||
NEXT_PRIVATE_SMTP_USERNAME: 'smtp-user'
|
||||
NEXT_PRIVATE_SMTP_PASSWORD: 'smtp-password'
|
||||
NEXT_PRIVATE_SIGNING_PASSPHRASE: 'cert-passphrase'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: documenso
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: documenso
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
containers:
|
||||
- name: documenso
|
||||
image: documenso/documenso:latest
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: documenso-config
|
||||
- secretRef:
|
||||
name: documenso-secrets
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
failureThreshold: 30
|
||||
volumeMounts:
|
||||
- name: signing-cert
|
||||
mountPath: /opt/documenso/cert.p12
|
||||
subPath: cert.p12
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: signing-cert
|
||||
secret:
|
||||
secretName: documenso-signing-cert
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
selector:
|
||||
app.kubernetes.io/name: documenso
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: '50m'
|
||||
cert-manager.io/cluster-issuer: 'letsencrypt-prod'
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- sign.example.com
|
||||
secretName: documenso-tls
|
||||
rules:
|
||||
- host: sign.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: documenso
|
||||
port:
|
||||
name: http
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: documenso
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: documenso
|
||||
namespace: documenso
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: documenso
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Full configuration reference
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Set up document signing
|
||||
- [Storage Configuration](/docs/self-hosting/configuration/storage) - Configure S3-compatible storage
|
||||
- [Email Configuration](/docs/self-hosting/configuration/email) - Configure SMTP providers
|
||||
- [Backups](/docs/self-hosting/maintenance/backups) - Database backup strategies
|
||||
- [Upgrades](/docs/self-hosting/maintenance/upgrades) - Upgrade procedures
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: Manual Deployment
|
||||
description: Deploy Documenso on a Linux server without Docker using Node.js and systemd.
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Step, Steps } from 'fumadocs-ui/components/steps';
|
||||
|
||||
<Callout type="warn">
|
||||
This guide is a community contribution and may not always reflect the latest changes. If you
|
||||
encounter issues, please refer to the [Docker](/docs/self-hosting/deployment/docker) or [Docker
|
||||
Compose](/docs/self-hosting/deployment/docker-compose) guides which are actively maintained.
|
||||
</Callout>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 20 or later
|
||||
- npm
|
||||
- PostgreSQL 14 or later
|
||||
- A Linux server (for systemd service setup)
|
||||
|
||||
## Install and Build
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
|
||||
<Step>
|
||||
### Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/documenso/documenso.git
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Configure environment variables
|
||||
|
||||
Navigate to the `documenso` folder and create a `.env` file:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Open the `.env` file and configure the required variables:
|
||||
|
||||
```bash
|
||||
NEXTAUTH_SECRET="your-secret-here"
|
||||
NEXT_PRIVATE_ENCRYPTION_KEY="your-encryption-key-min-32-chars"
|
||||
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY="your-secondary-key-min-32-chars"
|
||||
NEXT_PUBLIC_WEBAPP_URL="https://your-domain.com"
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL="http://localhost:3000"
|
||||
NEXT_PRIVATE_DATABASE_URL="postgresql://user:password@localhost:5432/documenso"
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL="postgresql://user:password@localhost:5432/documenso"
|
||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@your-domain.com"
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
If you use a reverse proxy in front of Documenso, set `NEXT_PUBLIC_WEBAPP_URL` to the public URL
|
||||
that users will access.
|
||||
</Callout>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Install dependencies and build
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run build
|
||||
npm run prisma:migrate-deploy
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
### Start the application
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
The server starts on `localhost:3000` by default. To use a different port, set the `PORT` environment variable in your `.env` file:
|
||||
|
||||
```bash
|
||||
PORT=3500
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Run as a systemd Service
|
||||
|
||||
Create a service file to run Documenso as a background service on Linux:
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/documenso.service
|
||||
|
||||
[Unit]
|
||||
Description=Documenso
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/var/www/documenso
|
||||
EnvironmentFile=/var/www/documenso/.env
|
||||
ExecStart=/usr/bin/node apps/remix/build/server/main.js
|
||||
TimeoutSec=15
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
The `EnvironmentFile` directive loads your `.env` file, so configuration (including `PORT`) is managed in one place. Alternatively, you can set environment variables directly in the service file with `Environment=` directives.
|
||||
|
||||
Enable and start the service:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable documenso
|
||||
sudo systemctl start documenso
|
||||
```
|
||||
|
||||
## Reverse Proxy with Nginx
|
||||
|
||||
A minimal Nginx configuration for proxying to Documenso:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
For production, configure SSL termination with Let's Encrypt or your preferred certificate
|
||||
provider.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Docker Deployment](/docs/self-hosting/deployment/docker) - Deploy using a standalone Docker container
|
||||
- [Docker Compose](/docs/self-hosting/deployment/docker-compose) - Deploy with Docker Compose
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - All configuration options
|
||||
- [Signing Certificate](/docs/self-hosting/configuration/signing-certificate) - Configure document signing certificates
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Deployment",
|
||||
"pages": ["docker", "docker-compose", "railway", "kubernetes", "manual"]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user