mirror of
https://github.com/documenso/documenso.git
synced 2026-07-16 07:58:35 +10:00
Merge branch 'main' into feat/acroform-field-import
This commit is contained in:
@@ -12,7 +12,7 @@ import { Callout } from 'fumadocs-ui/components/callout';
|
||||
| 21 CFR Part 11 | Compliant (Enterprise) |
|
||||
| SOC 2 | Compliant |
|
||||
| ISO 27001 | Planned |
|
||||
| HIPAA | Planned |
|
||||
| HIPAA | Compliant (Enterprise) |
|
||||
|
||||
## 21 CFR Part 11
|
||||
|
||||
|
||||
@@ -97,12 +97,12 @@ Documenso implements digital signatures with the following characteristics:
|
||||
- **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.
|
||||
For specific implementation details and configuration options, refer to the [signing certificates](/docs/concepts/signing-certificates) 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
|
||||
- [E-Sign Compliance](/docs/compliance/esign) - Legal frameworks for electronic signatures
|
||||
- [Signing Certificates](/docs/concepts/signing-certificates) - Certificate configuration
|
||||
- [Signing Workflow](/docs/concepts/signing-workflow) - Document activity and audit trail
|
||||
|
||||
@@ -167,5 +167,5 @@ To enable sequential signing:
|
||||
|
||||
## 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
|
||||
- [Add Recipients](/docs/users/documents/add-recipients) - How to add recipients to a document
|
||||
- [Field Types](/docs/concepts/field-types) - Learn about the different field types you can assign to recipients
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Common Errors
|
||||
description: A comprehensive troubleshooting matrix for Documenso API and Webhook integration errors.
|
||||
---
|
||||
|
||||
This guide provides a comprehensive troubleshooting matrix for the standard error codes returned by the Documenso API. Use this reference to diagnose and resolve integration issues related to envelopes, recipients, and webhooks.
|
||||
|
||||
## Application Error Codes
|
||||
|
||||
| Error Code | Description | Recommended Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `ALREADY_EXISTS` | The resource you are attempting to create already exists. | Verify if the entity (e.g., user, envelope, webhook) has already been instantiated. Use a `PUT` or `PATCH` request to update the existing resource instead of `POST`. |
|
||||
| `EXPIRED_CODE` | The provided access code or token has expired. | Generate a new access code or request a new invitation link before retrying the request. |
|
||||
| `INVALID_BODY` | The request payload is malformed. | Inspect your JSON payload structure. Ensure it strictly adheres to the expected schema and that no required fields are missing. |
|
||||
| `INVALID_REQUEST` | The overall request is malformed or invalid. | Review your API call parameters, including the URL, query parameters, and headers. Correct the request syntax. |
|
||||
| `RECIPIENT_EXPIRED` | The signing link or recipient access has expired. | Generate and resend a new invitation to the affected recipient. |
|
||||
| `LIMIT_EXCEEDED` | Your account usage quota has been exceeded. | Check your current plan limits. Upgrade your subscription or wait until your billing cycle renews. |
|
||||
| `NOT_FOUND` | The requested resource could not be found (404). | Verify the resource ID (envelope, document, webhook) passed in the URL. Ensure the resource has not been deleted. |
|
||||
| `NOT_IMPLEMENTED` | The requested feature is not currently supported by the server. | Consult the API documentation to verify available methods. Do not use this endpoint at this time. |
|
||||
| `NOT_SETUP` | The required configuration for this action is incomplete. | Access your account or integration settings and complete the necessary configuration before retrying. |
|
||||
| `INVALID_CAPTCHA` | Security token (Captcha) validation failed. | Ensure the Captcha token is correctly generated on the client side and transmitted without alteration in your request. |
|
||||
| `UNAUTHORIZED` | Missing or invalid authentication (401). | Verify that your API key is correct, active, and properly formatted in the `Authorization` header (e.g., `Bearer <YOUR_API_KEY>`). |
|
||||
| `FORBIDDEN` | Access to the resource is denied (403). | Ensure your API key or user account has the necessary permissions and roles to execute this specific action. |
|
||||
| `UNKNOWN_ERROR` | An unexpected internal server error occurred (500). | Retry the request later. If the issue persists, contact technical support with your request payload and the timestamp of the incident. |
|
||||
| `RETRY_EXCEPTION` | The operation failed temporarily but can be retried. | Implement an automatic retry logic in your integration, ideally using an exponential backoff strategy. |
|
||||
| `SCHEMA_FAILED` | Strict data schema validation failed. | Verify that the data types sent (string, number, boolean) exactly match the OpenAPI specification. |
|
||||
| `TOO_MANY_REQUESTS` | Rate limit exceeded (429). | Reduce the frequency of your API calls. Implement rate-limiting handling based on the response headers. |
|
||||
| `TWO_FACTOR_AUTH_FAILED` | Two-factor authentication (2FA) failed. | Verify the provided 2FA code. Ensure it was entered correctly and has not expired. |
|
||||
| `WEBHOOK_INVALID_REQUEST` | The webhook-related request is invalid. | Check your receiving endpoint configuration. Ensure the URL is correct and that your server accepts `POST` requests from Documenso. |
|
||||
|
||||
## Envelope State Errors
|
||||
|
||||
The following errors occur when attempting to perform actions on an envelope that are incompatible with its current state.
|
||||
|
||||
| Error Code | Description | Recommended Action |
|
||||
| :--- | :--- | :--- |
|
||||
| `ENVELOPE_DRAFT` | The action cannot be performed because the envelope is still in a draft state. | Finalize the envelope configuration and transition it to the `PENDING` (sent) state before attempting this operation. |
|
||||
| `ENVELOPE_COMPLETED` | The action cannot be performed because the envelope is already completed. | No further modifications (e.g., adding signers, modifying documents) can be made to an envelope once the signing process is finished. |
|
||||
| `ENVELOPE_REJECTED` | The action cannot be performed because the envelope was rejected by a recipient. | The signing flow is permanently halted. Create a new envelope if you wish to resubmit the document. |
|
||||
| `ENVELOPE_LEGACY` | The action cannot be performed because the envelope uses an obsolete format. | This envelope was created with a legacy version of the system. Recreate the envelope using the current API version to interact with it. |
|
||||
|
||||
## See Also
|
||||
|
||||
- [Documents API](/docs/developers/api/documents)
|
||||
- [Webhooks](/docs/developers/webhooks)
|
||||
@@ -1,4 +1,14 @@
|
||||
{
|
||||
"title": "API Reference",
|
||||
"pages": ["documents", "recipients", "fields", "templates", "teams", "rate-limits", "versioning", "developer-mode"]
|
||||
"pages": [
|
||||
"documents",
|
||||
"recipients",
|
||||
"fields",
|
||||
"templates",
|
||||
"teams",
|
||||
"rate-limits",
|
||||
"versioning",
|
||||
"developer-mode",
|
||||
"common-errors"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Storage Configuration
|
||||
description: Configure file storage for uploaded documents and signed PDFs using database storage (default) or S3-compatible object storage.
|
||||
description: Configure file storage for uploaded documents and signed PDFs using database storage (default), S3-compatible object storage, or Azure Blob Storage.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
@@ -10,10 +10,11 @@ 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 |
|
||||
| Backend | Best For | Scalability | Configuration |
|
||||
| ------------ | --------------------------------------- | ----------- | ------------- |
|
||||
| `database` | Small deployments, simplicity | Limited | None required |
|
||||
| `s3` | Production, large files, backups | High | Required |
|
||||
| `azure-blob` | Production on Azure, native Blob access | High | Required |
|
||||
|
||||
Select the storage backend with the `NEXT_PUBLIC_UPLOAD_TRANSPORT` environment variable:
|
||||
|
||||
@@ -23,6 +24,9 @@ NEXT_PUBLIC_UPLOAD_TRANSPORT=database
|
||||
|
||||
# S3-compatible storage
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=s3
|
||||
|
||||
# Azure Blob Storage (native)
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=azure-blob
|
||||
```
|
||||
|
||||
---
|
||||
@@ -283,6 +287,111 @@ NEXT_PRIVATE_UPLOAD_REGION=us-east-1
|
||||
|
||||
---
|
||||
|
||||
## Azure Blob Storage
|
||||
|
||||
Azure Blob Storage is supported as a native transport (not S3-compatible). Documenso uses the official `@azure/storage-blob` SDK and signs SAS URLs with the Storage Account key for browser uploads and downloads.
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description |
|
||||
| --------------------------------------- | ------------------------------------------------- |
|
||||
| `NEXT_PUBLIC_UPLOAD_TRANSPORT` | Set to `azure-blob` |
|
||||
| `NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_NAME` | Azure Storage Account name |
|
||||
| `NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_KEY` | Azure Storage Account access key |
|
||||
| `NEXT_PRIVATE_UPLOAD_AZURE_CONTAINER` | Container name where uploads are stored |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
|
||||
| `NEXT_PRIVATE_UPLOAD_AZURE_ENDPOINT` | Custom Blob endpoint URL. Useful for local development against Azurite (for example `http://127.0.0.1:10000`). | `https://<account>.blob.core.windows.net` |
|
||||
|
||||
### Azure Setup
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<Steps>
|
||||
<Step>
|
||||
### Create a Storage Account and Container
|
||||
|
||||
Create a Storage Account in the Azure Portal or via the Azure CLI, then create a container inside it:
|
||||
|
||||
```bash
|
||||
az storage account create \
|
||||
--name yourstorageaccount \
|
||||
--resource-group your-rg \
|
||||
--location eastus \
|
||||
--sku Standard_LRS
|
||||
|
||||
az storage container create \
|
||||
--name documenso-documents \
|
||||
--account-name yourstorageaccount
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure CORS on the container
|
||||
|
||||
The browser uploads documents directly to Azure Blob using a SAS URL, and downloads them the same way, so the Storage Account needs CORS rules that allow your application origin:
|
||||
|
||||
```bash
|
||||
az storage cors add \
|
||||
--services b \
|
||||
--methods GET PUT \
|
||||
--origins https://your-documenso-domain.com \
|
||||
--allowed-headers "Content-Type" "x-ms-blob-type" "Authorization" \
|
||||
--exposed-headers "*" \
|
||||
--max-age 3600 \
|
||||
--account-name yourstorageaccount
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
### Configure Environment Variables
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=azure-blob
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_NAME=yourstorageaccount
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_KEY=your-account-key
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_CONTAINER=documenso-documents
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Local Development with Azurite
|
||||
|
||||
Azurite is the official Azure Storage emulator. It supports the Blob REST API with account-key authentication.
|
||||
|
||||
```bash
|
||||
docker run -d --name azurite \
|
||||
-p 10000:10000 -p 10001:10001 -p 10002:10002 \
|
||||
mcr.microsoft.com/azure-storage/azurite
|
||||
```
|
||||
|
||||
Create the container against the well-known development account:
|
||||
|
||||
```bash
|
||||
az storage container create \
|
||||
--name documenso-documents \
|
||||
--connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
|
||||
```
|
||||
|
||||
Configure environment variables to point at the emulator:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT=azure-blob
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_NAME=devstoreaccount1
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_ACCOUNT_KEY=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_CONTAINER=documenso-documents
|
||||
NEXT_PRIVATE_UPLOAD_AZURE_ENDPOINT=http://127.0.0.1:10000
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
The Azurite key shown above is the public well-known development key, published by Microsoft for emulator use. Never reuse it in production.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## CloudFront CDN (Optional)
|
||||
|
||||
Use Amazon CloudFront to serve documents with lower latency and reduced S3 costs. CloudFront integration uses signed URLs for secure access.
|
||||
|
||||
@@ -116,11 +116,12 @@ export const EditorFieldDropdownForm = ({
|
||||
}
|
||||
|
||||
const newValues = [...currentValues];
|
||||
const removedValue = currentValues[index].value;
|
||||
newValues.splice(index, 1);
|
||||
|
||||
form.setValue('values', newValues);
|
||||
|
||||
if (form.getValues('defaultValue') === newValues[index].value) {
|
||||
if (form.getValues('defaultValue') === removedValue) {
|
||||
form.setValue('defaultValue', undefined);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -372,7 +372,7 @@ export const SignInForm = ({
|
||||
siteKey={turnstileSiteKey}
|
||||
options={{
|
||||
size: 'flexible',
|
||||
appearance: 'interaction-only',
|
||||
appearance: 'always',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -511,7 +511,7 @@ export const SignInForm = ({
|
||||
siteKey={turnstileSiteKey}
|
||||
options={{
|
||||
size: 'flexible',
|
||||
appearance: 'interaction-only',
|
||||
appearance: 'always',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -331,7 +331,7 @@ export const SignUpForm = ({
|
||||
siteKey={turnstileSiteKey}
|
||||
options={{
|
||||
size: 'flexible',
|
||||
appearance: 'interaction-only',
|
||||
appearance: 'always',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -178,7 +178,7 @@ export const ClaimAccount = ({ defaultName, defaultEmail }: ClaimAccountProps) =
|
||||
siteKey={turnstileSiteKey}
|
||||
options={{
|
||||
size: 'flexible',
|
||||
appearance: 'interaction-only',
|
||||
appearance: 'always',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ export const DocumentSigningMobileWidget = () => {
|
||||
layoutId="document-signing-mobile-widget-progress-bar"
|
||||
className="absolute inset-y-0 left-0 bg-primary"
|
||||
style={{
|
||||
width: `${100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
width: `${requiredRecipientFields.length === 0 ? 100 : 100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ export const DocumentSigningPageViewV1 = ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sortedRecipients = allRecipients.sort((a, b) => {
|
||||
const sortedRecipients = [...allRecipients].sort((a, b) => {
|
||||
// Sort by signingOrder first (nulls last), then by id
|
||||
if (a.signingOrder === null && b.signingOrder === null) {
|
||||
return a.id - b.id;
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ export const DocumentSigningPageViewV2 = () => {
|
||||
layoutId="document-flow-container-step"
|
||||
className="absolute inset-y-0 left-0 bg-primary"
|
||||
style={{
|
||||
width: `${100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
width: `${requiredRecipientFields.length === 0 ? 100 : 100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -294,7 +294,7 @@ export const EnvelopeSigningProvider = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const sortedRecipients = envelope.recipients.sort((a, b) => {
|
||||
const sortedRecipients = [...envelope.recipients].sort((a, b) => {
|
||||
// Sort by signingOrder first (nulls last), then by id
|
||||
if (a.signingOrder === null && b.signingOrder === null) {
|
||||
return a.id - b.id;
|
||||
|
||||
@@ -334,8 +334,6 @@ export const EnvelopeSignerPageRenderer = ({ pageData }: { pageData: PageRenderD
|
||||
fieldGroup.add(loadingSpinnerGroup);
|
||||
await signField(field.id, payload);
|
||||
}
|
||||
|
||||
loadingSpinnerGroup.destroy();
|
||||
})
|
||||
.finally(() => {
|
||||
loadingSpinnerGroup.destroy();
|
||||
|
||||
@@ -238,7 +238,7 @@ export const AdminOrganisationsTable = ({
|
||||
}}
|
||||
>
|
||||
{(table) =>
|
||||
!hidePaginationUntilOverflow || 1 > table.getPageCount() ? (
|
||||
!hidePaginationUntilOverflow || table.getPageCount() > 1 ? (
|
||||
<DataTablePagination additionalInformation="VisibleCount" table={table} />
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -40,6 +40,6 @@ export const handleInitialsFieldClick = async (
|
||||
|
||||
return {
|
||||
type: FieldType.INITIALS,
|
||||
value: initials,
|
||||
value: initialsToInsert,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user