## Summary
- Adds a new admin action to move a subscription (and Stripe customerId)
from one organisation to another owned by the same user
- The target organisation must be on the free plan (no active
subscription) — enforces paid → free only
- The source organisation's claim is reset to the free plan after the
move
## How it works
A "Move Subscription" option appears in the actions dropdown of the
organisations table (on the admin user detail page) for any org with an
active or past-due subscription. Clicking it opens a dialog where the
admin selects a target org from a filtered list of eligible (free-plan)
orgs owned by the same user.
The backend performs the swap atomically in a single Prisma transaction:
1. Deletes any stale inactive subscription on the target org
2. Moves the `customerId` from source to target org
3. Reassigns the `Subscription` record to the target org
4. Copies claim entitlements to the target org
5. Resets the source org's claim to FREE
No Stripe API calls are made — the Stripe subscription and customer
remain unchanged; only the DB-level org association is updated.
## Files changed
- **New:**
`packages/trpc/server/admin-router/swap-organisation-subscription.types.ts`
— Zod schemas
- **New:**
`packages/trpc/server/admin-router/swap-organisation-subscription.ts` —
Admin mutation
- **New:**
`apps/remix/app/components/dialogs/admin-swap-subscription-dialog.tsx` —
Dialog component
- **Modified:** `packages/trpc/server/admin-router/router.ts` — Register
route
- **Modified:**
`apps/remix/app/components/tables/admin-organisations-table.tsx` — Add
action menu item
Replace use() with useEffect/useState pattern to prevent 'async Client
Component' errors in Next.js 16 / React 19. Also strip colons from
useId() output which broke mermaid's render().
## Description
Resolves an issue where individual plan customers who cancel are not
correctly put down to the free plan.
To resolve this, we delete the subscription on the stripe subscription
delete webhook. Since the customerId is stored on the organisation they
can still access their old invoices.
## Description
Fixes a race condition in the envelope editor when opening "Send
Document" immediately after moving/resizing a selected field
Replication
1. Move or resize a field (do not blur the selector/quickbar that
appears when a field is selected)
2. Directly click the "Send Document" dialog
3. Error appears
Note: Step 2 needs to happen relatively fast after step 1 since this is
a race against the flush debouncer
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This PR introduces automatic detection and placement of fields and
recipients based on PDF placeholders.
The placeholders have the following structure:
- `{{fieldType,recipientPosition,fieldMeta}}`
- `{{text,r1,required=true,textAlign=right,fontSize=50}}`
When the user uploads a PDF document containing such placeholders, they
get converted automatically to Documenso fields and assigned to
recipients.
This PR introduces bulk actions for documents, allowing users to select
multiple envelopes and perform actions such as moving or deleting 1 or
more documents simultaneously.
The prompt option was being discarded for OAuth authorize URLs after
adding support for the NEXT_PRIVATE_OIDC_PROMPT env var. This meant
select_account (used elsewhere) was not being passed through.
Now defaults prompt to the provided option (or 'login'), and only
overwrites it when a valid OIDC prompt env var is set. Also adds a
type guard to validate the env var value.
The contact form accepts teamId as an optional param based on
where the user clicks "Support" from. Previously, when opened
from a non-team context, the null teamId would be parsed to NaN
and fail validation, causing the form to error out. Now the
validation only runs when a teamId is actually provided.
This PR fixes a bug in the `/api/v2/template` endpoint where the
pagination parameter `perPage` was being ignored. Previously, the
endpoint would return all matching templates regardless of the requested
limit, which could lead to performance issues and incorrect API
behavior.
API users were inadvertently sending documents without signature fields,
causing confusion for recipients and breaking their signing flows.
- Add getRecipientsWithMissingFields helper in recipients.ts
- Add server-side validation in sendDocument to block distribution
- Fix v1 API to return 400 instead of 500 for validation errors
- Consolidate UI signature field checks to use isSignatureFieldType
- Add E2E tests for both v1 and v2 APIs
Migrate from @documenso/pdf-sign and @cantoo/pdf-lib to @libpdf/core
for all PDF manipulation and signing operations. This includes:
- New signing transports for Google Cloud KMS and local certificates
- Consolidated PDF operations using libpdf API
- Added TSA (timestamp authority) helper for digital signatures
- Removed deprecated flatten and insert utilities
- Updated tests to use new PDF library
The v9 packages are deprecated. This updates to v13 which includes
breaking API changes: optionsJSON wrapper for auth functions,
renamed properties (authenticator→credential), and base64 encoding
for credential IDs via isoBase64URL helper.
Adds OpenCode support for AI-assisted development, including custom
commands and skills to help contributors maintain consistency and
streamline common workflows.
#### Changes
- Added "AI-Assisted Development with OpenCode" section to
CONTRIBUTING.md with:
- Installation instructions and provider configuration
- Documentation for 8 custom commands (/implement, /continue,
/interview, /document, /commit, /create-plan, /create-scratch,
/create-justification)
- Typical workflow guide
- Clear policy that AI-generated code must be reviewed before submission
- Added .agents/ directory for plans, scratches, and justifications
- Added .opencode/ commands and skills for the agent
- Added helper scripts for creating agent files
Generate certificates and audit logs using Konva instead of browserless.
This should:
- Reduce the changes of generations failing
- Improve sealing speed
We use playwright + chromium for certificate generation
and other things.
Self-hosters often have an issue with generating certificates
due to the base image not coming with chromium for size purposes.
This adds a new `-chromium` tag to our docker images for downloading
the larger bundled chromium containers.
When using an API key created in a team context, the
documents/templates’ owner always defaults to the team API token
creator, rather than the actual uploader.
For example, John creates the API key for the team "Lawyers". Tom and
Maria use the API key to upload documents. All the uploaded documents
are attributed to John.
This makes it impossible to see who actually uploaded a document.
The new feature allows users to enable document ownership delegation
from the organization/team settings.
Seems there's a memory leak in gpu rendering with skia canvas
where contexts can live for much longer than expected escaping gc
cleanup
CPU rendering seems better albeit a bit slower.
Synthetic tests were ran with `--expose-gc` to simulate load over time.
When flattening PDF forms, some appearance streams lack the required
/Subtype /Form dictionary entry needed when used as XObjects. This
causes
corruption in Adobe Reader which fails to render these flattened fields.
Per PDF spec, Form XObject streams require:
- /Subtype /Form (required)
- /FormType 1 (optional)
The normalizeAppearanceStream function ensures these entries exist
before
adding appearance streams as XObjects to the page content stream.
Fixes rendering issues where flattened fields don't display in PDF
viewers.
Add fullName prop to signature pad components to automatically populate
typed signature
field with signer's name. Updates signature dialog, type component, and
all signing forms
across embed, document, template, and envelope flows to pass through the
user's full
name for better user experience.
Add proper cleanup for PDF.js pages and loading task to prevent memory
leaks when
processing multiple PDF pages. Ensure page cleanup is called after each
page is
rendered and both PDF document and loading task are properly destroyed
with error
handling.
Previously you had to have explicit knowledge of the
feature and enable it in order to use AI assisted field
detection.
This surfaces it by having a secondary dialog prompting
for enablement.
Also includes a fix for CC recipients not getting marked
as signed in weird edge cases.
Upgrade the `@react-email/render` package to handle
suspense during renders.
We could have just swapped to `renderAsync` for the 0.0.x
version of the package but it's better to upgrade as part
of this change.
CI has been run locally and emails have been verified to
work and render as expected in our local mail trap.
## Description
Workaround until we can commit directly to main for translation
extractions
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Adds documentation for the recently added AI features
Includes details for how users can enable AI features for their team or
organisation
Also includes details for how self-hosters can setup their instance to
allow for AI features
## Description
Adds the following languages since we updated our PDF sealing to support
special characters
- Japanese
- Korean
- Chinese (Simplified)
## Tests
Ran through the signing process in these new languages.
We were previously omitting cmaps meaning that
when signing documents with certain UTF-8 characters or CJK characters
they would appear as outlined squares in the pdf viewer despite the
actual pdf looking as expected with the characters displaying correctly.
Currently on inngest the sealing task fails during decoration stating
that it can not find the step "xxx"
My running theory is that this was due to it being a
Promise.all(map(...)) even though that isn't explicitly disallowed.
This change turns it into a for loop collecting promises to be awaited
after the fact.
Local inngest testing looks promising.
This pull request refactors the filtering logic in the `getTeamCounts`
function within `get-stats.ts` to improve consistency and
maintainability. The main change is the consolidation of multiple filter
conditions into a single `AND` clause, which now includes search
filters, folder filters, and visibility filters. This ensures that all
relevant filters are applied in a unified way for document count
queries.
This PR is handles the changes required to support envelopes. The new
envelope editor/signing page will be hidden during release.
The core changes here is to migrate the documents and templates model to
a centralized envelopes model.
Even though Documents and Templates are removed, from the user
perspective they will still exist as we remap envelopes to documents and
templates.
Use the `select_account` prompt for SSO OIDC to avoid constantly asking
for credentials to be entered with a client has an existing session with
the SSO provider.
Improve the email sent to invite a user to approve/sign/assist/view a
document.
The current links "Reject Document" / "Sign Document" confuse some users
as they think the document will be rejected/signed just by clicking on
these buttons.
This change makes it more clear that they will have a chance to view the
document before they can reject/sign.
Adds the missing `viewedDocument` method call for embedded signing.
I believe this had previously been added but was lost as the result of a
merge conflict being resolved.
The checkboxes were difficult to see and the "Send reminder" button
wasn't disabled when no recipients were selected. This PR disables the
sending button when there's no selected recipient and improves the
checkboxes visibility.
Allow organisations to manage an SSO OIDC compliant portal. This method
is intended to streamline the onboarding process and paves the way to
allow organisations to manage their members in a more strict way.
When sending a document for signing, emails for recipients are sent
before the document status is updated.
In this case, the job "send.signing.requested.email" fails because it
cannot find the document with a PENDING status.
Improves the mobile signing UX making actions available via the floating
navbar more obvious.
Also adds an automatic switch to the complete button once all fields
have been signed.
On larger documents we could accidentally start trying to render fields
while not all pages of the PDF have loaded due to us checking for a
single page existing. This would cause an error to be thrown, hard
locking those documents.
This change resolves this by grabbing the highest page number from the
given fields and using it for the visibility check instead.
This PR improves the mobile experience of the document signing page by
implementing a collapsible widget design for the signing form. On mobile
devices, the form now appears as a fixed bottom sheet that can be
expanded/collapsed, while maintaining the sticky sidebar layout on
desktop.
Currently the Stripe customer name is set to the organisation name,
which in some cases is just the organisation name.
This update makes it so it uses the owner name instead.
adds document download functionality to the API v2, returning
pre-signed S3 URLs that provide secure, time-limited access to document
files similar to what happens in the API v1 download document endpoint.
## Description
General fixes to the email domain features
Changes made:
- Add "email" validation for "Reply-To email" fields
- Fix issue where you can't remove the "Reply-To" email after it's set
- Fix issue where setting the "Sender email" back to Documenso would
still send using the org/team pref
Certificates have suddenly become blank when using browserless and
Chrome CDP.
This change introduces a workaround that involves reloading the
certificate pdf. Which is hacky but seems to work for now, a better
solution should be found in the future.
Currently new lines are not rendered in text fields correctly on the
`/sign` page. This is an issue because when the field is inserted and
sealed we respect new lines.
Prevent users from creating a separate organisation for individual
plans. Only applies to users who have 1 personal organisation and are
subscribing to the "Individual" plan.
The reason for this change is to keep the layout in the "Personal" mode
which means it doesn't show a bunch of unusable "organisation" related
UI.
The resend document API was not working correctly when filtering
recipients. The query was filtering recipients at the database level,
which could result in an empty recipients array being returned even when
the document had recipients. This prevented the API from properly
identifying which recipients needed reminder emails.
Currently users who cancel their plan are stuck without the ability to
resubscribe. This allows them to choose a plan to subscribe
This assumes that a Subscription in the "INACTIVE" state means that the
plan has been paid but canceled.
No tests have been done to determine the relation between "PAST_DUE" and
"INACTIVE" states within our context.
## Description
Add a new document audit log to detect when the document is viewed. This
should only be visible in the document audit log page
Notes:
1. I wanted to reuse the `DOCUMENT_OPENED` event and add an additional
paramter to track sequential views, but it's not query-able
2. This will log "DOCUMENT_VIEWED" before "DOCUMENT_OPENED" but i don't
think it matters
Adds password reauthentication to our existing reauth providers,
additionally swaps from an exclusive provider to an inclusive type where
multiple methods can be selected to offer a this or that experience.
During our field rework that makes fields appear
more accurately between signing and the completed pdf we swapped to
using text fields. Unfortunately as part of that we dropped using the
Noto font for the text field causing ANSI encoding issues when
encountering certain characters.
This change restores the font and handles a nasty issue we had with our
form flattening reverting our selected font.
Adds document access tokens and QR code functionality to enable secure
document sharing via URLs. It includes a new document access page that
allows viewing and downloading documents through tokenized links.
This pull request updates the implementation of the admin leaderboard,
enhancing data handling and improving type safety. It introduces clearer
differentiation between users and teams, adds additional fields to track
more relevant information, and refactors the querying logic to optimize
performance and maintainability.
This pull request removes the `id` field from
`IsDocumentPlatformOptions` in `is-document-platform.ts` and updates the
billing logic in `create-team.ts`: platform plan users create their
first team free, but pay for subsequent teams; non-platform users need
an active team subscription if billing is enabled.
## Description
Previously we weren't passing the DocumentMeta to our readonly field
component which is used for displaying completed fields by other
recipients.
Due to this dates that were not using the default format were displaying
as invalid date adding confusion to the signing process.
## Related Issue
Reported via support email.
## Changes Made
- Pass the document meta to the readonly field component.
- Support showing completed fields within the embedding UI.
## Testing Performed
- Manual testing
Fix webhooks being sent twice due to duplicate frontend calls
Updated the assistant confirmation dialog so the next signer is always
visible (if dictate is enabled). Because if the form is invalid (due to
no name) there is no visual queue that the form is invalid (since it's
hidden)
## Notes
Didn't bother to remove the weird assistants form since it currently
works for now

## Tests
- Currently running locally
- Tested webhooks via network tab and via webhook.site
Adds next recipient dictation functionality to document signing flow,
allowing assistants and signers to update the next recipient's
information during the signing process.
## Related Issue
N/A
## Changes Made
- Added form handling for next recipient dictation in signing dialogs
- Implemented UI for updating next recipient information
- Added e2e tests covering dictation scenarios:
- Regular signing with dictation enabled
- Assistant role with dictation
- Parallel signing flow
- Disabled dictation state
## Testing Performed
- Added comprehensive e2e tests covering:
- Sequential signing with dictation
- Assistant role dictation
- Parallel signing without dictation
- Form validation and state management
- Tested on Chrome and Firefox
- Verified recipient state updates in database
## Description
The changes in `sign-direct-template.tsx` automatically fill in field
values for text, number, and dropdown fields when default values are
present or if the fields are read-only. In `checkbox-field.tsx`, the
changes fix the checkbox signing by checking if the validation is met
and improving how it saves or removes checkbox choices.
## Testing Performed
I tested the code locally with a variety of documents/fields.
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
Improves the existing document rejection process by actually marking a
document as completed cancelling further actions.
## Related Issue
N/A
## Changes Made
- Added a new rejection status for documents
- Updated a million areas that check for document completion
- Updated email sending, so rejection is confirmed for the rejecting
recipient while other recipients are notified that the document is now
cancelled.
## Testing Performed
- Ran the testing suite to ensure there are no regressions.
- Performed manual testing of current core flows.
Currently Stripe prices search is omitting a price for an unknown
reason.
Changed our fetch logic to use `list` instead of `search` allows us to
work around the issue.
It's unknown on the performance impact of using `list` vs `search`
The `typedSignatureEnabled` prop was removed from the `SignatureField`
component, which broke the typed signature meaning that nobody could
sign documents by typing their signature.
Introduces the ability for users with the **Assistant** role to prefill
fields on behalf of other signers. Assistants can fill in various field
types such as text, checkboxes, dates, and more, streamlining the
document preparation process before it reaches the final signers.
The global team settings weren't created when creating a new team.
## Changes Made
The global team settings are now created when a new team is created.
Implements a bulk send feature allowing users to upload a CSV file to
create multiple documents from a template. Includes CSV template
generation, background processing, and email notifications.
Adds the ability to align text to the left, center or right for relevant
fields.
Previously text was always centered which can be less desirable.
See attached debug document which has left, center and right text
alignments set for fields.
<img width="614" alt="image"
src="https://github.com/user-attachments/assets/361a030e-813d-458b-9c7a-ff4c9fa5e33c"
/>
N/A
- Added text align option
- Update the insert in pdf method to support different alignments
- Added a debug mode to field insertion
- Ran manual tests using the debug mode
When authenticating using OIDC some IDPs send additional fields in their
authorization response.
This leads to an error because these fields can't be persisted to the DB
through the auth.js prisma adapter.
This PR solves this by deleting all unrecognized fields from the
authorization response before persisting.
This behaviour is also compliant to
[RFC6749 Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2)
Adds the ability to upload a custom document when using a template.
This is useful when you have a given fixed template with placeholder
values that you want to decorate with Documenso fields but will then
create a final specialised document when sending it out to a given
recipient.
Improves the display of the recipients status when
distribution method is set to none.
Previously we had a ton of checks for sendStatus = SENT
which will never trigger when distributing manually causing
some confusion.
Checkboxes were previously styled super wonky due to
the usage of checkboxClassName which styled the checkbox
trigger.
This change reverts all that and leaves it with sensible defaults
across dark and light mode.
Passes the timezone of the user uploading the document/template via the
UI to the server.
If the user uploads a document/template via the API and doesn't set a
timezone, it defaults to `Etc/UTC`.
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
adds a `select-none` class to the signature pad in order to
prevent iPadOS from becoming too trigger happy with the context menu and
auto corrects. Please ensure this doesn't break anything by accident.
Adds a new `ownerDocumentCompleted` to the email settings that controls
whether a document will be sent to the owner upon completion.
This was previously the only email you couldn't disable and didn't
account for users integrating with just the API and Webhooks.
Also adds a flag to the public `sendDocument` endpoint which will adjust
this setting while sendint the document for users who aren't using
`emailSettings` on the `createDocument` endpoint.
To be able to use the PATCH `/api/v1/documents/{id}/fields/{fieldId}`
endpoint, we need to know the fields ID of a particular document. The
issue #1178 suggested to create a new endpoint for this. To be
consistent with the `/templates` endpoint, I propose in this PR to
directly add the `fields` field to the `/documents/:id` endpoint.
## Description
I added the option of downloading a document without the signing
certificate for teams. They can disable/enable the option in the
preferences tab.
The signing certificate can still be downloaded separately from the
`logs` page.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
- **New Features**
- Enhanced German, Spanish, French, and Polish translations for various
document management and marketing phrases.
- **Improvements**
- Updated translations for clarity and accuracy across multiple
languages, including user notifications and document actions.
- Added new translation entries to improve user experience and
localization in the Documenso application.
- **Chores**
- Updated revision dates in translation files to reflect recent changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
## Description
Allows for smaller field sizing in addition to improving our styling
when displaying labels on smaller fields.
This is the minimum currently supported field size until we perform a
more extensive refactor of our current drag and drop system.
## Related Issue
Reported via support channels
## Changes Made
- Updated our minimum size constraints
- Attempted to add a general autosizing component for text and failed
- Updated styling in a bunch of places to use the css `clamp()` method
for dynamic sizing.
## Description
Adds a dialog that will display when a certain field threshold is
reached asking the user if they would like to sign non-critical fields
such as name, date, initials, and email with information that is already
available.
This has not been added to direct templates since we would often not
have all the pre-requisite knowledge since users are mostly anonymous.
Additionally, this has not been added to the embedding view since it may
detract from the experience for some.
Will not prompt the user if there is action authentication on the
document.
See the below demo:
https://github.com/user-attachments/assets/71739b5c-1323-4da9-89fd-a1145c9714d5
## Related Issue
#1281 (Older PR relating to the feature)
## Changes Made
- Added a new auto-sign dialog that will automatically trigger once
certain criteria is met.
## Testing Performed
- Tested that the dialog displays when the threshold is met
- Tested that the dialog is hidden when the threshold is not met
- Tested that the messaging during errors is correct
- Tested that the dialog does not display when 2FA or Passkeys are
required
## Description
When using 2fa enabled authentication on direct templates we run into an
issue where a 2fa token has been attached to a field but it's submitted
at a later point.
To better facilitate this we have introduced the ability to have a
window of valid tokens.
This won't affect other signing methods since tokens are verified
immediately after they're entered.
## Related Issue
N/A
## Changes Made
- Updated our validate2FAToken method to use a window based approach
rather than the default verify method.
## Testing Performed
- Created a series of tokens and tested upon different intervals and
windows to confirm functionality works as expected.
## Description
Adds support for rejecting a given document informing the document
owner.
Flows for resolving a rejection don't currently exist so it's up to the
document owner to reach out to the recipient and work out a way to move
forward via a new document or offline agreement.
## Related Issue
## Changes Made
- Added new rejection properties to the recipient schema
- Added API endpoints to support rejection
- Added email templates for notifying the document owner and recipient
- Added a dialog on the signing page to start the rejection flow.
## Testing Performed
- Manually tested the flow end to end
- Automated tests are planned
## Description
This pull request introduces modifications to the GitHub Actions
workflow to ensure that the `latest` Docker tag is only pushed for
stable releases. It prevents the `latest` tag from being assigned to
release candidates (`-rc`), beta versions, or other pre-release tags.
This enhancement improves version management by keeping the `latest` tag
reserved exclusively for fully stable versions (e.g., `1.0.0`).
## Related Issue
Fixes#1404
## Changes Made
- Added a conditional check to verify if the release version follows the
format `X.Y.Z` (stable release format).
- Updated the Docker manifest creation steps to only push the `latest`
tag if the version is a stable full release.
- Modified both DockerHub and GitHub Container Registry push steps to
reflect the new versioning conditions.
## Description
Currently certificate translations on production sometimes does not show
the required language.
This could not be replicated when creating certificates on staging
(Browserless.io) and local development (Chromium), which means this fix
ultimately cannot be tested unless on live.
This is an attempt to fix it by isolating the certificate generation
into it's own context, and applying a cookie to define the required
language.
This fix is based on the assumption that there is some sort of error
which pushes the certificate to be generated on the client side, which
ultimately will render in English due to constraints on nextjs.
## Changes Made
- Apply language into cookie instead purely dynamically on SSR
- Minor unrelated fixes
## Testing Performed
Tested to ensure certificates could still be generated
## Description
This PR introduces global settings for teams. At the moment, it allows
team admins to configure the following:
* The default visibility of the documents uploaded to the team account
* Whether to include the document owner (sender) details when sending
emails to the recipients.
### Include Sender Details
If the Sender Details setting is enabled, the emails sent by the team
will include the sender's name:
> "Example User" on behalf of "Example Team" has invited you to sign
"document.pdf"
Otherwise, the email will say:
> "Example Team" has invited you to sign "document.pdf"
### Default Document Visibility
This new option allows users to set the default visibility for the
documents uploaded to the team account. It can have the following
values:
* Everyone
* Manager and above
* Admins only
If the default document visibility isn't set, the document will be set
to the role of the user who created the document:
* If a user with the "User" role creates a document, the document's
visibility is set to "Everyone".
* Manager role -> "Manager and above"
* Admin role -> "Admins only"
Otherwise, if there is a default document visibility value, it uses that
value.
#### Gotcha
To avoid issues, the `document owner` and the `recipient` can access the
document irrespective of their role. For example:
* If a team member with the role "Member" uploads a document and the
default document visibility is "Admins", only the document owner and
admins can access the document.
* Similar to the other scenarios.
* If an admin uploads a document and the default document visibility is
"Admins", the recipient can access the document.
* The admins have access to all the documents.
* Managers have access to documents with the visibility set to
"Everyone" and "Manager and above"
* Members have access only to the documents with the visibility set to
"Everyone".
## Testing Performed
Tested it locally.
## Description
Support setting a document language that will control the language used
for sending emails to recipients. Additional work has been done to
convert all emails to using our i18n implementation so we can later add
controls for sending other kinds of emails in a users target language.
## Related Issue
N/A
## Changes Made
- Added `<Trans>` and `msg` macros to emails
- Introduced a new `renderEmailWithI18N` utility in the lib package
- Updated all emails to use the `<Tailwind>` component at the top level
due to rendering constraints
- Updated the `i18n.server.tsx` file to not use a top level await
## Testing Performed
- Configured document language and verified emails were sent in the
expected language
- Created a document from a template and verified that the templates
language was transferred to the document
## Description
Adds user management capabilities to our current API. Allows for adding,
removing, listing and updating members of a given team using a valid API
token.
## Related Issue
N/A
## Changes Made
- Added an endpoint for inviting a team member
- Added an endpoint for removing a team member
- Added an endpoint for updating a team member
- Added an endpoint for listing team members
## Testing Performed
Tests were written for this feature request
Add the ability to insert typed signatures.
Once the signature field is placed on the document, a checkbox appears
in the document editor where the document owner can allow signers to add
typed signatures. Typed signatures are disabled by default.

`later`
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced comprehensive documentation for the Documenso public API.
- Added detailed guides for the following API endpoints:
- Uploading documents
- Generating documents from templates
- Adding fields to documents
- Included example payloads and response structures for better
understanding.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
We got cal.com 🚀
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new blog post detailing Cal.com's use of Documenso for
enhancing DPA and BAA processes.
- The blog outlines the challenges and solutions related to compliance
document management.
- Features images and captions to illustrate key points and improve
engagement.
- **Documentation**
- Added insights into Cal.com's journey as an open-source scheduling
solution and their commitment to transparency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Currently merge conflicts arise due to the compiled JS and PO
translation files.
This PR is a rework on how we handle extracting and compiling
translations to streamline PRs and merging branches.
## Changes Made
- Remove compiled translation files from being committed
- Extract and compile translations only on build
- Extract will still occur when commits land on main to sync and pull
new translations with Crowdin

A client requested it, and it makes sense showing the full count.
This is how it was before.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Updated document status tab counts to display actual numbers without
capping at 99 or using '+' symbols.
- **Bug Fixes**
- Improved clarity and accuracy of document status counts in the user
interface.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new blog post titled "Go Fork Yourself," discussing the
philosophy of open-source software and the significance of forking
within the OSS community, along with real-world examples and an
invitation for reader engagement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Before

After

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new font style for signature fields, enhancing visual
distinction.
- Increased text size for signature fields to improve prominence.
- **Bug Fixes**
- Adjusted the text size for signature display on larger screens for
better visual hierarchy.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
It used the wrong property for finding the document's dateFormat in the
`DATE_FORMATS` array.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Improved date format selection in the settings form to ensure accurate
formatting based on value.
- Default timezone now automatically set to the user's local timezone
for better user experience.
- **Bug Fixes**
- Corrected initialization of the timezone field to enhance form
accuracy.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
prisma customer story on blog
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a customer story blog post detailing why Prisma chose
Documenso for their signing needs, highlighting four key reasons.
- Added author information and metadata for enhanced content engagement.
- Included links to additional resources and social media for reader
interaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Currently the language cookie is set to session, so restarting browser
will reset it.
This sets the expiry for two years.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced language preference functionality with extended cookie
lifespan for improved user experience.
- **Bug Fixes**
- Resolved issues related to cookie expiration for language settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Adds the ability for the document owner to edit recipients and their
fields after the document has been sent.
A recipient can only be updated or deleted if:
- The recipient has not inserted any fields
- Has not completed the document
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
- **New Features**
- Added new localization messages to clarify user actions regarding
document signing.
- Enhanced French translations for improved user interaction.
- **Improvements**
- Updated localization strings in German and English for clearer
feedback on signer and recipient statuses.
- Improved overall structure of localization files for better
maintainability.
- **Dependency Updates**
- Upgraded `next-axiom` and `remeda` libraries to their latest versions,
potentially enhancing performance and stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mythie <me@lucasjamessmith.me>
## Description
Add initial French translations
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added support for the French language, enhancing accessibility for
French-speaking users.
- Introduced localized French messages for various application
functionalities, improving user experience.
- **Bug Fixes**
- Minor formatting updates in French translation files to remove
extraneous newline characters.
- **Chores**
- Updated line references in French translation files to maintain
alignment with code changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@documenso.com>
## Description
Currently invalid or missing `accept-language` headers will cause issues
rendering Plural components since we do not validate them.
This adds a check to try filter out invalid locales.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced locale filtering process to ensure only valid locales are
processed.
- **Bug Fixes**
- Improved data integrity by preventing invalid locales from affecting
application functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Currently this won't always display super well since
our insertion solution isn't amazing but our current
minimum bounds within the UI are a bit large and can be
smaller.
This change makes it smaller and uses container queries to
support dynamically displaying labels based on the container
size.
Adds the ability to specify an optional signing order for documents.
When specified a document will be considered sequential with recipients
only being allowed to sign in the order that they were specified in.
## Description
Web changes:
- Enabled i18n for web
- Add option to change language in command menu
- Add option to change language in menu-switcher
Web and marketing changes:
- Stop setting 'en' preference into cookie if the user's language is not
supported
- Dropped middleware changes
- Rotated cookie from 'i18n' to 'language'
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a language switcher in the footer for improved language
selection.
- Added dynamic language change functionality in the command menu.
- Implemented a dropdown menu item for quick access to the language
switcher.
- **Bug Fixes**
- Resolved issues related to language change notifications and state
management.
- **Translations**
- Added new translation entries for improved language support, including
"Search languages..." in English and German.
- Updated existing translations to enhance clarity and accuracy.
- **Chores**
- Simplified internationalization handling in middleware.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: github-actions <github-actions@documenso.com>
Templates can be created and sent with advanced fields that have empty
values. That will cause an error when the user tries to sign the
document.
For example, you can create a template with a checkbox field and save
it. Then, you can click the "Use template" button and send the document
by clicking "Send document." However, this shouldn't be possible if the
advanced field doesn't have any values.
Add the authOptions property to the document and
recipient related API endpoints.
These were previously missing so the only way API
users could set the authOptions was via templates
and using the generateTemplate endpoint.
## Description
Seems like I was overconfident in #1323 and I did not test properly.
Currently, the advanced settings for a field in a **template** is not
pre-filled with the current fieldMeta, although it is correctly
pre-filled in a **draft document** (That's probably where I messed up my
testing).
In this PR, I propose to directly use the fieldMeta provided by the
field prop in `FieldAdvancedSettings`, instead of multiplying tRPCs to
request the fieldMeta while we already have it.
I apologize for the wasted time in reviewing my previous PR which was
only correcting the display of the field label in the template view.
## Testing Performed
- This time, I correctly checked that the advanced settings for a field
is correctly pre-filled both in a document draft and in a template.
- `npm run build` builds correctly.
When the Select field has a default value, it automatically signs with
it. If you change it, you need to refresh the page to re-sign again with
that value. This PR improves the UX by making the default value
"selectable" in the dropdown menu.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Updated the `DropdownField` component to simplify the handling of
default values, ensuring the dropdown starts without a pre-selected
option.
- Improved the clarity of the placeholder text in the dropdown,
enhancing user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new `CallToAction` component to enhance user engagement
in the self-hosting documentation.
- Added interactive call-to-action elements in the self-hosting
documentation pages to guide users towards specific actions.
- **Documentation**
- Updated self-hosting documentation to include the new call-to-action
feature, improving usability and interactivity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Refactor the current date formatting system to utilize Lingui.
## Changes Made
- Remove redundant `LocaleData` component with Lingui dates
## Important notes
For the internal pages for certificates, default to en-US to format any
dates.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced internationalization support across various components by
utilizing the `i18n` object for date formatting.
- Streamlined locale management by removing cookie-based language
handling and adopting a more centralized approach.
- **Bug Fixes**
- Improved date formatting consistency by replacing the `LocaleDate`
component with direct calls to `i18n.date()` in multiple components.
- **Documentation**
- Updated localization strings in the `web.po` files to reflect recent
changes in the source code structure.
- **Chores**
- Minor formatting adjustments and code organization improvements across
various files to enhance readability and maintainability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: github-actions <github-actions@documenso.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Chores**
- Updated the metadata revision dates in multiple German translation
files for consistency.
- Added extraneous blank lines in multiple translation files for cleaner
formatting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Fixes#1318 and allows to submit a partial `fieldMeta` when updating a
field instead of replacing the current `fieldMeta`, to be consistent
with other endpoints.
Fixes#1259. I literally just used the exact same line of code than from
the document page so it is consistent.
The bug is extremely frustrating because if you make change to the
fields meta on the template and refresh the page, you can't see field
meta (label, default value, required), and neither do your co-workers.
Of course, I tested the change and it now displays correctly the
existing field meta on the template page.
## Description
<!--- Describe the changes introduced by this pull request. -->
In this PR, I've fixed a small grammar error.
## Related Issue
Fixes: #1315
## Changes Made
It replaces `"wont"` with `"won't"` and `"Only your signing experience
is"` with `"Only your signing experience will be"`
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [x] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [x] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Updated text in the Document Share Button for enhanced clarity and
grammatical accuracy.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: ajeet <109718740+ajeetcode@users.noreply.github.com>
Previously we would delete all invites and confirmation tokens upon
completing the action that they represent.
This change instead adds a flag on each token indicating whether it has
been completed so we can action a
completed token differently in the UI to reduce confusion for users.
This had been brought up a number of times where confirmation emails,
team member invites and other items
may have been actioned and forgotten about causing an error toast/page
upon subsequent revisit.
Previously dialogs would be closed upon refocusing the browser tab due to router refetches occuring which would cause data-table columns to re-render. This is now resolved by extracting the column definitions outside of the returning render and into a memo hook.
## Description
Configure the advanced field via API.
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced API functionality to support field metadata during field
creation.
- Introduced validation checks for field metadata to ensure necessary
information is provided for advanced field types.
- **Bug Fixes**
- Improved error handling during field creation to return properly
formatted error responses.
- **Documentation**
- Updated API schemas to include field metadata, enhancing data
validation and response structures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
When signing a document the final signer is often
greeted with a super long completing spinner since
we are synchronously signing the document and sending
emails to all recipients. This is frustrating and
has caused issues for customers and self-hosters.
Moving sealing to a background job resolves this
and improves the overall snappiness of the app while
also supporting retrying the sealing if it were to
fail in the future.
This has the implication of a document no longer
immediately being in a "completed" state once all
signers have signed. To assist with this we now
refetch the page every 5 seconds upon signing
completion until the document status as shifted to
completed.
Previously, it wasn't possible to download an audit log of a document
uploaded by another user because the function used the ID of the user
making the request to retrieve the document. However, the document
uploaded by another user has that user's ID, not the ID of the user
making the request.
Adds support for an `externalId` query param to be passed when linking a
user to a direct template. This external id will then be stored on the
document upon signing completion.
Since we allow checkboxes and radio fields without a label (which we use for the value) we
had an issue where multiple checkboxes with no value would exist and items would not end
up checked on the resulting document.
This change fixes that by adding a placeholder value for these empty checkboxes and labels.
This change will allow for user registration when users are federated
through oidc provider even if the general signup is disabled
additionally the users email address can now be automatically set as
trusted. This will force corporate users to signin using SSO instead of
creating manual accounts.
Updates the current regex based approach for validating redirect urls to
instead use the native URL constructor which is available in browsers and
Node.js and handles several valid cases that were previously not working.
fair use policy and support page
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added a "Support" documentation page outlining various support options
for users, including Community, Paid Account, and Enterprise support.
- Introduced a "Fair Use Policy" documentation page, providing
guidelines and reassurance regarding usage of the service.
- Enhanced the account creation documentation with additional
information about the Fair Use Policy.
- **Improvements**
- Updated the documentation structure with new sections for easier
navigation, including "Support" and "Fair Use Policy."
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
chore: translation typo
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced the `Header` component for improved internationalization by
enabling translation of the "Documentation" text.
- **Documentation**
- Added comments in translation files to link translation entries to the
`Header` component, improving maintainability and traceability for
future updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced clarity and accuracy of translation strings for the German
and Romanian languages, including the Enterprise License description.
- **Bug Fixes**
- Corrected translation errors in both German and Romanian, ensuring
proper grammar and terminology.
- **Chores**
- Updated revision dates across multiple translation files to reflect
the latest changes for better tracking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@documenso.com>
## Description
This PR introduces an initial i18n implementation using
[Lingui](https://lingui.dev).
We plan to combine it with Crowdin which will provide AI translations
when PRs are merged into main.
We plan to rollout i18n to only marketing for now, and will review how
everything goes before continuing to introduce it into the main
application.
## Reasoning
Why not use i18n-next or other alternatives?
To hopefully provide the best DX we chose Lingui because it allows us to
simply wrap text that we want to translate in tags, instead of forcing
users to do things such as:
- Update the text to `t('some-text')`
- Extract it to the file
- The text becomes a bit unreadable unless done correctly
Yes, plugins such as i18n-ally and Sherlock exist to simplify these
chores, but these require the user to be correctly setup in vscode, and
it also does not seem to provide the required configurations for our
multi app and multi UI package setup.
## Super simple demo
```html
// Before
<p>Text to update</p>
// After
<p>
<Trans>Text to update</Trans>
</p>
```
## Related Issue
Relates to #885 but is only for marketing for now.
Another branch is slowly being prepared for the changes required for the
web application while we wait to see how this goes for marketing.
## Changes Made
Our configuration does not follow the general standard since we have
translations that cross:
- Web app
- Marketing app
- Constants package
- UI package
This means we want to separate translations into:
1. Marketing - Only translations extracted from `apps/marketing`
2. Web - Only translations extracted from `apps/web`
3. Common - Translations from `packages/constants` and `packages/ui`
Then we bundle, compile and minify the translations for production as
follows:
1. Marketing = Marketing + Common
2. Web = Web + Common
This allows us to only load the required translations when running each
application.
Overall general changes:
- Add i18n to marketing
- Add core i18n setup to web
- Add pre-commit hook and GH action to extract any new <Trans> tags into
the translation files
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added Romanian localization for marketing messages to improve
accessibility for Romanian-speaking users.
- Introduced German and English translation modules and PO files to
enhance the application's internationalization capabilities.
- Integrated internationalization support in the RootLayout component
for dynamic language settings based on server-side configurations.
- Enhanced the Enterprise component with translation support to adapt to
user language preferences.
- Added a `<meta>` tag to prevent Google from translating the page
content, supporting internationalization efforts.
- **Bug Fixes**
- Resolved minor issues related to the structure and accessibility of
translation files.
- **Chores**
- Updated project dependencies to support the new localization features
and ensure stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@documenso.com>
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
Corrects grammar on `/pricing` page of main website.
## Related Issue
N/A
## Changes Made
Added the word "for" (grammar)
Pluralized "offer" to "offers" (grammar)
## Testing Performed
No testing should be required for this change.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [x] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [x] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved grammatical accuracy in the Enterprise component, enhancing
readability and professionalism.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Added functionality to decline team invitations
- Added email notifications for when team is deleted
- Added email notifications for team members joining and leaving
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added "Documentation" and "Support" links to the footer for enhanced
resource accessibility.
- Updated the "Changelog" link position for improved navigation.
- **Bug Fixes**
- Replaced dynamic URL handling with a direct link to API documentation
for clarity and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Documentation**
- Enhanced project README for clarity and improved environment variables
section.
- Added comprehensive developer and user documentation, including guides
on local development, public API, self-hosting, and compliance
standards.
- Introduced specific guides for contributing, creating API keys, using
webhooks, and setting up security measures.
- Detailed documentation on various fields available for document
signing to improve user understanding.
- Added metadata structuring to improve navigation within the
documentation site.
- **Chores**
- Updated `.gitignore` to better handle project files.
- **New Features**
- Introduced detailed metadata and documentation for various Documenso
functionalities, including signing documents, user profiles, and
compliance levels.
- Added functionality for Direct Link Signing, enabling easy sharing for
document signing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Timur Ercan <timur.ercan31@gmail.com>
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Co-authored-by: David Nguyen <davidngu28@gmail.com>
## Description
Adds the external ID column to documents and templates with an option to
configure it in the API or UI.
External ID's can be used to link a document or template to an external
system and identify them via webhooks, etc.
Introduces the ability to use anonymous SMTP authentication where no username or password is provided.
Also introduces a new flag to disable TLS avoiding cases also where STARTTLS is used despite `secure` being
set to `false`
Introduces customization options for the document completion email
template to allow for custom email bodies and subjects for documents
created from direct templates.
## Testing Performed
- Verified correct rendering of custom email subject and body for direct
template documents
- Verified the all other completed email types are sent correctly
chore Top 3 Signing Efficiency Hacks for Freelancers
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a blog post titled "Top 3 Signing Efficiency Hacks for
Freelancers" with tips on streamlining contract signing using Documenso.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
changelog for v 1.5.6
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Display document creation time in the documents view.
- Introduced direct template links for easier access.
- Added support for OpenID Connect (OIDC) for improved authentication
options.
- **Enhancements**
- Transitioned to a new rust-based library for signing documents,
enhancing performance and security.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
announcing profiles blogpost
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced "Documenso Profiles" that allow users to share digital
signature templates publicly.
- Recipients can sign documents directly via public links, streamlining
the signing process.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
How to Sign an NDA online (fast) blog article
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Published a new blog post on signing NDAs faster with Documenso,
highlighting the use of direct links, templates, and basic signing for
efficiency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
**Pending testing on preview**
Enable yearly team pricing during checkout.
## Changes Made
- Enable selecting yearly team pricing during checkout
- Update pricing on marketing to $480 for yearly team plan
## Testing Performed
- Stripe simulations
Pending manual testing
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Updated the savings message in the pricing table to reflect new
savings amounts.
- Adjusted yearly pricing from $500 to $480.
- Modified user addition costs based on subscription periods.
- **Improvements**
- Simplified button text in the Create Team Checkout Dialog to
consistently display "Checkout".
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
When using account required auth for a given document this change now
shows the sign up or sign in button depending on if an account actually
exists within
Documenso.
This change should reduce friction and confusion when a recipient has
been invited to a document.
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
- Updated the ssr for the signing page to fetch whether a user account
exists when document auth is required
- Pass the user exists flag to the document auth required page
## Testing Performed
- Created and sent a document to a user with an account and one without an account in both a logged in and logged out state and confirmed that the dialogs are displayed as expected.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced user statistics display including metrics on document
creation and signing in the admin dashboard.
- Added a bar chart visualizing monthly growth data of users with
documents.
- **Enhancements**
- Updated dashboard stats page to provide more detailed user-related
metrics and charts.
- Added new CSS variable `--gold` for consistent theming across light
and dark modes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Show a dialog when the document has signers with no signature fields
placed.
## Changes Made
Created a new dialog that'll be triggered when the document owner tries
to send a document to the signers without placing signature fields. The
document owners can't proceed to the next step unless they add signature
fields.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
https://github.com/documenso/documenso/assets/25515812/f1b5c34e-2ce0-40e3-804c-f05d23045710
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced "Direct Links" for async signing, allowing users to create
documents from templates using public links.
- Added `MissingSignatureFieldDialog` component to ensure users don't
miss adding signature fields.
- **Enhancements**
- Updated blog content to provide guidance on contract management and
announce new pricing plans.
- **Bug Fixes**
- Improved async signing process for better efficiency and control.
- **Refactor**
- Improved internal code structure and import order for stripe-related
functionality.
- **Tests**
- Enhanced e2e tests to verify signature presence before document
creation and updated test flows for document approval.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: David Nguyen <davidngu28@gmail.com>
## Description
This pull request introduces the functionality to display pending fields
on the document page view. This enhancement allows users to see which
fields are pending and need to be completed.

## Changes Made
- Added `getPendingFieldsForDocument` function in
`packages/lib/server-only/field/get-pending-fields-for-document.ts` to
fetch pending fields for a document.
- Created a new component `DocumentPendingFields` in
`document-pending-fields.tsx` to display the pending fields with options
to hide individual fields.
## Testing Performed
- Tested the new feature by creating documents with pending fields and
verifying their display on the document page view.
- Verified that the pending fields are correctly hidden when the "Hide
field" button is clicked.
- Ran unit tests for the new functionality and existing components to
ensure no regressions.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [x] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
No additional notes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced logic for handling pending and completed document fields
based on signing status.
- **Refactor**
- Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`.
- Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`.
- **Bug Fixes**
- Improved field retrieval accuracy and display based on recipient
signing status.
- **Style**
- Enhanced UI elements with new icons and button adjustments for better
user interaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: David Nguyen <davidngu28@gmail.com>
## Description
Currently a work in progress but this adds background tasks to Documenso
for handling tasks that take a little longer and aren't part of the
overall critical path.
## Related Issue
N/A
## Changes Made
Added a pluggable jobs provider supporting both a local provider and a
Trigger.dev provider.
## Testing Performed
- Verified job level retries work
- Verified task level retries work without incrementing job retries


**This will still have a lot of rough edges but is a perfect MVP for
local _durable_ compute which may accept more providers later on**
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added background job management functionalities to handle email
sending and job processing.
- Introduced support for triggering and managing jobs using various
providers.
- **Chores**
- Updated environment configuration files to support new job management
features.
- Added and updated scripts and dependencies for job management and CLI
commands.
- **Refactor**
- Refactored email sending functionalities to utilize the new job queue
mechanism.
- **Documentation**
- Updated documentation to reflect the new environment variables and
configurations related to background jobs.
- **Configuration**
- Updated Docker configurations to support new services for job
management.
- Updated VSCode settings for improved development experience with new
dependencies and formatting rules.
- **Database**
- Added new database schemas and migrations to support background job
management and tracking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
documenso for freelancer contracts article
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Published a new blog post: "How Documenso Enhances Contract Management
for Freelancers, Helping Them Close More Clients Efficiently." This post
offers insights into contract management and the benefits of digital
signatures using Documenso.
- **Documentation**
- Updated a hyperlink in the conclusion section of the blog post titled
"How Documenso Helps Freelancers Close More Clients Efficiently" to
direct users to a new URL related to signing freelance contracts with
Documenso.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
added changelog page to marketing site
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a changelog page to detail recent updates and features.
- Added a link to the changelog in the website footer for easy access.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
When using account required auth for a given document
this change now shows the sign up or sign in button
depending on if an account actually exists within
Documenso.
This change should reduce friction and confusion when a
recipient has been invited to a document.
direct links account blogarticle
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced Direct Links for templates, allowing asynchronous document
signing via unique shareable links for easy access.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
"How Documenso Helps Freelancers Close More Clients Efficiently" blog
article
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Documentation**
- Added a new blog post titled "How Documenso Helps Freelancers Close
More Clients Efficiently," discussing the benefits of using Documenso to
streamline the proposal process with digital signatures for enhanced
speed and security in closing deals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Adjusts the styling to improve the layout and appearance of the carousel
controls and thumbnails on smaller screens.
## Changes Made
- Updated the carousel control styling to be more responsive.
- Adjusted the font size and padding of the slide component for better
display on mobile screens.
## Screenshot

## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
PDFs can have pages that are rotated, which are correctly rendered in
the frontend. However, when we load the PDF in the backend, the rotation
is applied which causes issues when we want to insert fields.
To account for this, we swap the width and height for pages that are
rotated by 90/270 degrees. This is so we can calculate the virtual
position the field was placed if it was correctly oriented in the
frontend.
Then when we insert the fields, we apply a transformation to the
position of the field so it is rotated correctly.
**Test document with 0/90/180/270 rotated pages**
[rotated_test.pdf](https://github.com/user-attachments/files/15798138/rotated_test.pdf)
chore: sunset early adopters
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new `Carousel` component with video autoplay
functionality.
- Added a `Thumb` component for carousel navigation.
- Introduced a new `Enterprise` component detailing enterprise licensing
and compliance.
- **Updates**
- Updated the `Hero` component to replace the `Widget` with the new
`Carousel`.
- Revised the `PricingTable` component to reflect new pricing plans and
details.
- Changed tooltip text in the marketing app for better clarity.
- Updated button texts and links in the `Callout` component to promote
the "Free Plan".
- **Content**
- Published a blog post announcing the end of the Early Adopters Plan
and introducing new pricing plans.
- **Dependencies**
- Added Embla Carousel dependencies for enhanced carousel functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Modifies the generated filename for the downloaded PDF by appending
"_signed" to the base title.
## Changes Made
- Update the filename in the downloadFile function call to append
"_signed" to the baseTitle variable.
## Testing Performed
Tested the `downloadPDF` function locally to ensure that the downloaded
PDF file has the correct filename with "_signed" appended.
## Description
Update the direct template signing process and emails to correctly
reflect the role of the recipient who actioned the direct template.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Dynamic updating of title and description based on recipient role in
the document signing process.
- Enhanced email templates to include recipient roles, providing more
context in email notifications.
- **Improvements**
- More descriptive actions in email templates based on recipient roles,
improving clarity for recipients.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Direct templates links is a feature that provides template owners the
ability to allow users to create documents based of their templates.
## General outline
This works by allowing the template owner to configure a "direct
recipient" in the template.
When a user opens the direct link to the template, it will create a flow
where they sign the fields configured by the template owner for the
direct recipient. After these fields are signed the following will
occur:
- A document will be created where the owner is the template owner
- The direct recipient fields will be signed
- The document will be sent to any other recipients configured in the
template
- If there are none the document will be immediately completed
## Notes
There's a custom prisma migration to migrate all documents to have
'DOCUMENT' as the source, then sets the column to required.
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
## Description
This PR adds generic OIDC as an authentication provider. This allows
personal users and companies potentially to define whatever IdP they
want as long as it supports the OIDC well known format. (Azure, Zitadel,
Authentik, KeyCloak, Google, etc. all support it)
## Related Issue
Fixes#1090
## Changes Made
- Adds OIDC buttons to the signin and registration pages
- Adds appropriate environment variables
- Adds migration to add OIDC to the `IdentityProvider` Enum
## Testing Performed
#### Zitadel
- Created application in Zitadel as an web app, with Client auth
- Enabled `User Info inside ID Token` in Token settings
- Copied client id and client secret to the new .ENV variables
- Copied the well-known URL from the URLs section to .ENV
- Created new account with OIDC provider button
- Verified email manually
- Signed into account with OIDC provider
- Logged out
- Signed into accounting again with OIDC provider
#### Authentik
- Created application in Authentik
- Copied client id and client secret to the new .ENV variables
- Copied the well-known URL from the URLs section to .ENV
- Created new account with OIDC provider button
- Verified email manually
- Signed into account with OIDC provider
- Logged out
- Signed into accounting again with OIDC provider
#### Azure AD
- Created application in Azure AD using OAuth2
- Copied client id and client secret to the new .ENV variables
- Copied the well-known URL from the URLs section to .ENV
- Created new account with OIDC provider button
- Verified email manually
- Signed into account with OIDC provider
- Logged out
- Signed into accounting again with OIDC provider
Adds fields to the Account model to support various pieces
of data returned by OIDC providers such as AzureAD and GitLab.
Additionally passes through the email verification status and handles
retrieving the email for providers such as AzureAD who use a different
claim instead.
Reverts the change to find-documents to use Kysely. I'd like to gain
confidence by using it in smaller pieces before commiting to doing
what is one of our most complicated queries in Documenso.
Introduces the ability to not send an email when sending
(publishing) a document using the API.
Additionally returns the signing link for each recipient
when working with recipient API endpoints and returns
the document object including recipients when sending
documents via API.
A series of changes to improve sealing robustness avoiding errors that
have appeared during monitoring.
Additionally handles the recently published CVE affecting the
`pdfjs-dist` library.
Bumps ReactPDF and pdfjs-dist to avoid the CVE that allows
for code execution in pdf's. This change doesn't specifically
upgrade to the latest pdfjs-dist due to issues with top level
await, instead disabling the evaluation of javascript within
the PDF.
Use Noto Sans to gracefully handle inserting custom text
on PDF's. Previously we were using Helvetica which is a
standard PDF font but that would fail for any character
that couldn't be encoded in WinANSI.
Noto Sans was chosen as it has support for a large number
of languages and glyphs with challenges now being adding
support for CJK glyphs.
Previously we used the form flattening method from PDF-Lib
but unfortunately when it encountered orphaned form items
or other PDF oddities it would throw an error.
Because of this certain documents would fail to seal and
be stuck in a pending state with no recourse available.
This change rewrites the form flattening handler to be
more lenient when coming across the unknown opting to skip
items it can't handle rather than abort.
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
Display time in 12h format in the documents table.
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
Fixes#1077
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Use DateTime.DATETIME_SHORT
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested time in different timezone.
- Ran tests in web and mobile browser
1. Login
2. Add a document
3. Verify that you see date and time in 12h format as per your locale.
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [x] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [x] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<img width="1512" alt="Screenshot 2024-04-29 at 04 21 53"
src="https://github.com/documenso/documenso/assets/29673073/778155d4-a920-40bd-acdc-7451c9c5d4b7">
<img width="300" alt="Screenshot 2024-04-29 at 04 22 18"
src="https://github.com/documenso/documenso/assets/29673073/3471de0f-f426-4ea1-be1e-220462aff9e4">
## Description
General enhancements for templates.
## Changes Made
Added the following changes to the template flow:
- Allow adding document meta settings
- Allow adding email settings
- Allow adding document access & action authentication
- Allow adding recipient action authentication
- Save the state between template steps similar to how it works for
documents
Other changes:
- Extract common fields between document and template flows
- Remove the title field from "Use template" since we now have it as
part of the template flow
- Add new API endpoint for generating templates
## Testing Performed
Added E2E tests for templates and creating documents from templates
**Description:**
This PR updates and adds a new action to assign `status: assigned` label
---------
Signed-off-by: Adithya Krishna <aadithya794@gmail.com>
## Description
Update the template flow to allow for entering recipient placeholder
emails and names
## Changes Made
- General refactoring
- Added advanced recipient settings for future usage
## Description
Currently if you complete a pending encrypted document, it will prevent
the document from being sealed due to the systems inability to decrypt
it.
This PR disables uploading any documents that cannot be loaded as a
temporary measure.
**Note**
This is a client side only check
## Changes Made
- Disable uploading documents that cannot be parsed
- Refactor putFile to putDocumentFile
- Add a flag as a backup incase something goes wrong
## Description
Currently users who sign in via Google SSO do not get assigned a Stripe
customer account.
This enforces the Stripe customer requirement on sign in.
There might be a better place to put this so it's open to any
suggestions.
## Description
Improves the sealing process by being strict on how long certificate
generation can take, opting to fail generation and continue sealing.
Also changes the ordering of sealing so an error in the process won't
also cause a document to be "COMPLETED" since it hasn't been
cryptographically sealed yet.
The downside to this change is that documents that fail during sealing
will require manual intervention as a signer or owner won't be able to
*complete* the document.
## Testing Performed
- Modified code to force specific failure modes to occur and verified
that documents were either gracefully sealed without a certificate or
not sealed and not completed.
Improves the sealing process by being strict on how
long certificate generation can take, opting to fail
generation and continue sealing.
Also changes the ordering of sealing so an error in the
process won't also cause a document to be "COMPLETED"
since it hasn't been cryptographically sealed yet.
The downside to this change is that documents that fail
during sealing will require manual intervention as a signer
or owner won't be able to *complete* the document.
## Description
Currently deleting a pending document where you are a recipient off will
delete the document, but will also throw an error.
This is due to the recipient being updated after the document deleted,
which is only supposed to happen for completed documents.
## Description
Automatically marks the document as completed if all the recipients are
CC.
## Changes Made
Added an if statement in the last form step (`onAddSubjectFormSubmit`)
that checks if all the recipients are CC. If so, the document status is
updated to `COMPLETED`.
## Testing Performed
Tested the changes and they work as expected.
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced document sending logic to update document status based on
recipient roles.
- **Bug Fixes**
- Removed redundant form submission handling in the document editing
feature.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Feature flags are broken on SSR due to this error
```
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11731:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: RequestContentLengthMismatchError: Request body length does not match content-length header
at write (node:internal/deps/undici/undici:8590:41)
at _resume (node:internal/deps/undici/undici:8563:33)
at resume (node:internal/deps/undici/undici:8459:7)
at [dispatch] (node:internal/deps/undici/undici:7704:11)
at Client.Intercept (node:internal/deps/undici/undici:7377:20)
at Client.dispatch (node:internal/deps/undici/undici:6023:44)
at [dispatch] (node:internal/deps/undici/undici:6254:32)
at Pool.dispatch (node:internal/deps/undici/undici:6023:44)
at [dispatch] (node:internal/deps/undici/undici:9343:27)
at Agent.Intercept (node:internal/deps/undici/undici:7377:20) {
code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
}
}
```
I've removed content-length header since it isn't mandatory to my
knowledge for get requests.
## Changes
- Add fallback local flags when individual flag request fails
- Add error logging
- Remove `content-length` from headers being passed to Posthog
## Description
Upgrade playwright once again and use CDP for remote connections to
avoid version lock-in with `playwright.connect`
Resolves the issue where all CI is failing currently due to downgrading
playwright.
## Description
Currently if you create a team webhook, you can see it in your personal
webhooks.
However, interacting with them will throw errors because there is server
side logic preventing any interaction with them since they are outside
of the correct context.
So the solution is to either:
- Allow the user to edit the team webhooks that they created on their
personal webhooks page
- Hide team webhooks for personal webhooks pages
This PR goes with the second option, but is open to suggestions.
## Description
Added the ability for recipients to see fields from other recipients who
have completed the document when they are signing the document
Added the ability for the document owner to see fields from recipients
who have completed the field on the document page view (only visible
when the document is pending)
## 🚨🚨 Migrations🚨🚨
- Drop all `Fields` that do not have a `Recipient` set (not sure how it
was possible in the first place)
- Remove optional `Recipient` field on `Field` which doesn't make sense
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Enhanced document viewing by adding read-only fields based on document
status.
- Improved signing page by fetching and displaying completed fields for
tokens.
- Updated avatar component to show recipient status with tooltips for
better user interaction.
- **Bug Fixes**
- Made `recipientId` a required field in the database to ensure data
consistency.
- **Refactor**
- Optimized popover functionality in UI components for better
performance and user experience.
- **Documentation**
- Added detailed component and function descriptions for new features in
the system.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Disable the document certificate download button when the document is
not complete
## Changes Made
- Disable UI button
- Disable TRPC API endpoint
## Testing Performed
Tested locally for pending, draft and completed documents
**Description:**
This PR adds a check for filename title and if the title ends with
`.pdf` then the extension isnt added or else its added
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Enhanced email attachment handling to ensure PDF files are correctly
identified with a ".pdf" extension.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Allow users to download a completed document via API.
## Testing Performed
Tested the code locally by trying to download both draft and completed
docs. Works as expected.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Implemented functionality to download signed documents directly from
the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Updated the email content based on whether the document owner is a
recipient or not.
If the document owner is a recipient (self-signer):
* the email subject will be `Please view/sign/approve your document`
* the email header will be `Please view/sign/approve your document
"<your-doc-title>"`
* the email content will be `You have initiated the document
"<your-doc-title>" that requires you to view/sign/approve it.`
Otherwise:
* the email subject will be `Please view/sign/approve this document`
* the email header will be `<doc-owner> has invited you to
view/sign/approve "<doc-title>"`
* the email content will be `<doc-owner> has invited you to
view/sign/approve the document "<doc-title>".`
## Related Issue
Related to #1091
## Testing Performed
Tested the feature with a different number of recipients (including and
excluding the document owner - self-signer). Tested both the sending and
resending functionality.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## UI Screenshots




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Enhanced the document invitation components to support scenarios where
the recipient is also the sender, providing customized email content and
subject lines.
- Introduced new properties in email templates to improve clarity and
relevance based on the user's role in the document signing process.
- **Refactor**
- Updated components to use a more flexible `headerContent` property for
displaying invitation headers, replacing previous individual inviter
details.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Currently users can sign and complete draft documents, which will result
in a completed document in an invalid state.
## Changes Made
- Prevent recipients from inserting or uninserting fields for draft
documents
- Prevent recipients from completing draft documents
- Remove ability to copy signing tokens unless document is pending
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced document status visibility and control across various
components in the application. Users can now see and interact with
document statuses more dynamically in views like `DocumentPageView`,
`DocumentEditPageView`, and `DocumentsDataTable`.
- Improved document signing process with updated status checks, ensuring
actions like signing, completing, and removing fields are only available
under appropriate document statuses.
- **Bug Fixes**
- Adjusted document status validation logic in server-side operations to
prevent actions on incorrectly stated documents, enhancing the overall
security and functionality of document processing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Add additional information for each role to help document owners
understand what each role involves.
## Changes Made



## Testing Performed
Tested the changes locally.
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Updated recipient role terminology and added tooltips in the
`AddSignersFormPartial` component:
- "Signer" changed to "Needs to sign" with tooltip
- "Receives copy" changed to "Needs to approve" with tooltip
- "Approver" changed to "Needs to view" with tooltip
- "Viewer" changed to "Receives copy" with tooltip
- Enhanced select dropdown options with icons and tooltips for different
recipient roles in the `AddTemplatePlaceholderRecipients` component.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Timur Ercan <timur.ercan31@gmail.com>
## Description
Fix the issue where duplicate templates can be created
## Changes Made
- Prevent duplicate templates by correctly disabling elements
- Clear the PDF when the form is reset
- General UI changes to new template dialog for consistency
- Add description for new template dialog
- Add close button for new template dialog
## Testing Performed
Manual testing
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have followed the project's coding style guidelines.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced new components in the template creation dialog for better
usability: `DialogClose`, `DialogDescription`, and `DialogFooter`.
- Enhanced file upload handling and display.
- Added a cancel button and a create template button to the dialog
footer for improved navigation.
- **Refactor**
- Updated the dialog content and form layout for a more intuitive user
experience.
- Refactored form structure and labels for clarity.
- **Removed Features**
- Removed the `Label` component from the dialog.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Currently, when the command menu is opened using the Command+K hotkey,
two modals are getting rendered.
This is because the modals are mounted in two components: header and
desktop-nav. Upon triggering the hotkey, both modals are rendered.
## Related Issue
#1032
## Changes Made
The changes I made are in the desktop nav component. If the desktop nav
receives the command menu state value and the state setter function, it
will trigger only that. If not, it will trigger the state setter that is
defined in the desktop nav. This way, we are preventing the modal from
mounting two times.
## Testing Performed
- Tested behaviour of command menu in the portal
- Tested on browsers chrome, arc, safari, chrome, firefox
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced the navigation experience by integrating command menu state
management directly within the `DesktopNav` component, allowing for
smoother interactions and control.
- **Refactor**
- Simplified the handling of the command menu by removing the
`CommandMenu` component and managing its functionality within
`DesktopNav`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: David Nguyen <davidngu28@gmail.com>
## Description
This PR introduces the ability to add oneself as a signer by simply
clicking a button, rather than filling the details manually.
### "Add Myself" in the document creation flow
https://github.com/documenso/documenso/assets/25515812/0de762e3-563a-491f-a742-9078bf1d627d
### "Add Myself" in the document template creation flow
https://github.com/documenso/documenso/assets/25515812/775bae01-3f5a-4b24-abbf-a47b14ec594a
## Related Issue
Addresses
[#113](https://github.com/documenso/backlog-internal/issues/113)
## Changes Made
Added a new button that grabs the details of the logged-in user and
fills the fields *(email, name, and role)* automatically when clicked.
## Testing Performed
Tested the changes locally through the UI.
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced the ability for users to add themselves as signers within
documents seamlessly.
- **Enhancements**
- Improved form handling logic to accommodate new self-signer
functionality.
- Enhanced user interface elements to support the addition of self as a
signer, including a new "Add myself" button and disabling input fields
during the process.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Description:**
- Updated mobile header with respect to latest designs
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added a new `showText` property to the `MenuSwitcher` component to
control text visibility.
- Added a `textSectionClassName` property to the `AvatarWithText`
component for conditional text section styling.
- Updated the `CommandDialog` and `DialogContent` components with new
positioning and styling properties.
- **Style Updates**
- Adjusted text size responsiveness in the `Hero` component for various
screen sizes.
- Modified text truncation and input styling in the `Widget` component.
- Changed the width of the `SheetContent` element in `MobileNavigation`
and adjusted footer layout.
- **Documentation**
- Added instructions for certificate placement in `SIGNING.md`.
- **Refactor**
- Standardized type imports across various components and utilities for
improved type checking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Signed-off-by: Adithya Krishna <adithya@documenso.com>
Signed-off-by: Adithya Krishna <aadithya794@gmail.com>
Co-authored-by: David Nguyen <davidngu28@gmail.com>
The searchDocuments function is used for the shortcuts commands, afaik.
The function returns the documents that match the user query (if any),
alongside all their recipients.
The reason for that is so it can build the path for the document. E.g.
if you're the document owner, the document path will be
`..../documents/{id}`. But if you're a signer for example, the document
path (link) will be `..../sign/{token}`.
So instead of doing that on the frontend, I moved it to the backend.
At least that's what I understood. If I'm wrong, please correct me.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced the `CommandMenu` component to simplify search result
generation and improve document link management based on user roles.
- **Refactor**
- Updated document search logic to include recipient token masking and
refined document mapping.
- **Style**
- Minor formatting improvement in document routing code.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Refactor**
- Enhanced the API specification generation process to include operation
IDs, security schemes, and security definitions more efficiently.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a "Claim Account" feature allowing new users to sign up by
providing their name, email, and password.
- Enhanced user experience for both logged-in and non-logged-in users
with improved UI/UX and additional functionality.
- **Enhancements**
- Implemented form validation and error handling for a smoother sign-up
process.
- Integrated analytics to track user actions during account claiming.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Co-authored-by: David Nguyen <davidngu28@gmail.com>
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
Should fix the rerendering of the status widget
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
## Description
When document audit logs were first introduced, we by default set the
`fieldSecurity` to `NONE`
Now that document auth has been added, this is causing issues since we
do not use `NONE` to define field that has no migrations required, but
rather have the `fieldSecurity` field itself be undefined.
To keeps things consistent, this migration replaces `NONE` with
undefined.
There are a few ways to approach this:
- Run a prisma migration on the JSON
- Modify the data before we pass the data to the schema in
`parseDocumentAuditLogData`
- Use `NONE` instead of undefined
If anyone thinks there's a better way to do this, please drop a comment
🙇
## Description
General UI updates
## Changes Made
- Add consistent spacing between document edit/view/log pages
- Add document status to document audit log page
- Update document loading page to reserve space for the document status
below the title
- Update the document audit log page to show full dates in the correct
locale
## Description
Adds the ability to prefill native PDF form fields via the API using
either normal documents or templates.
Since we won't always know when a document is uploaded and has forms we
opt to do this on creation for templates and on sending the document to
recipients in all cases. This means that for a created document it can
look a little funky since the form fields are missing the data until the
document is sent.
This should be improved in a later change but since we've scoped this to
an API only workflow for now we are less concerned with the visual
issues.
## Related Issue
N/A
## Changes Made
- Added the `formValues` field the document model
- Added a new method for finding and filling form fields based on a `key
| value` pair
- Updated the API input shapes to take the new field.
## Testing Performed
- Have created and tested a document using the API both for creation and
usage with a template.
- Have verified that the fields display as expected either during
creation or sending depending on the document type.
## Description
Fetch the updated version of the document after sealing it and return
it. Previously, the `document.documentData.data` wasn't up to date. Now
it is.
## Related Issue
Fixes#1088.
## Testing Performed
* Added console.logs in the code to make sure it returns the proper data
* Set up a webhook and tested that the webhook receives the updated data
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Changes Made
- Refactor/optimise tests
- Reduce flakiness
- Add parallel tests (if there's enough CPU capacity)
- Removed explicit worker count when running parallel tests. Defaults to
50% of CPU capacity.
Might want to consider sharding the test across runners in the future as
our tests grows.
## Description
Fixed issue where accounts that were initially created via
email/password, then linked to an SSO account, can bypass the 2FA during
login if they use their email password.
## Testing Performed
Tested locally, and 2FA is now required for linked SSO accounts
## Description
Add the following document action auth options:
- 2FA
- Passkey
If the user does not have the required auth setup, we onboard them
directly.
## Changes made
Note: Added secondaryId to the VerificationToken schema
## Testing Performed
Tested locally, pending preview tests
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced components for 2FA, account, and passkey authentication
during document signing.
- Added "Require passkey" option to document settings and signer
authentication settings.
- Enhanced form submission and loading states for improved user
experience.
- **Refactor**
- Optimized authentication components to efficiently support multiple
authentication methods.
- **Chores**
- Updated and renamed functions and components for clarity and
consistency across the authentication system.
- Refined sorting options and database schema to support new
authentication features.
- **Bug Fixes**
- Adjusted SignInForm to verify browser support for WebAuthn before
proceeding.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Added a dialog button at the bottom of the admin/documents/[id] page
with confirmation popup.
Confirmation popup have validation for reason to input.
On confirmation document is deleted, and an email is triggred to the
owner of document with the reason stated.
Let me know if there is any more requirement or correction is needed in
this pr. :) #1020
## Description
Currently opening modals, clicking select boxes or using anything from
radix that overlays the screen in some way will shift the screen.
This can be easily noticeable when changing the document "Period"
selector on the /documents page.
## Changes Made
Undo the gutter change for now. Can find a proper solution another time.
https://github.com/documenso/documenso/assets/20962767/5bcae576-2944-4ae5-a2c3-0589e7f61bdb
This change flattens and normalizes annotation and widget layers within
the PDF document removing items that can be accidentally modified after
signing which would void the signature attached to the document.
Initially this change was just to assign to an ArcoForm object in the
document catalog if it existed but quickly turned into the above.
When annotations aren't flattened Adobe PDF will say that the signature
needs to be validated and upon doing so will become invalid due to the
annotation layers being touched.
To resolve this I set out to flatten and remove the annotations by
pulling out their normal appearances if they are present, converting
them into xobjects and then drawing those using the drawObject operator.
This resolves a critical issue the users experienced during the signing
flow when they had marked up a document using annotations in pdf
editors.
## Description
Currently if you try to load the document edit page when fields need to
be rendered, you will not be able to see the fields until you proceed to
the next step.
This is because the fields require the document PDF to be loaded prior
to rendering them.
This PR resolves that issue by only rendering the fields after the PDF
is loaded.
## Changes Made
- Add a state to track whether the PDF is loaded
- Render the fields only after the PDF is loaded
## Testing Performed
Tested document flow manually and the fields are rendered correctly on
load.
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have updated the documentation to reflect these changes, if
applicable.
## Description
**Fixes issues with mismatching state between document steps.**
For example, editing a recipient and proceeding to the next step may not
display the updated recipient. And going back will display the old
recipient instead of the updated values.
**This PR also improves mutation and query speeds by adding logic to
bypass query invalidation.**
```ts
export const trpc = createTRPCReact<AppRouter>({
unstable_overrides: {
useMutation: {
async onSuccess(opts) {
await opts.originalFn();
// This forces mutations to wait for all the queries on the page to reload, and in
// this case one of the queries is `searchDocument` for the command overlay, which
// on average takes ~500ms. This means that every single mutation must wait for this.
await opts.queryClient.invalidateQueries();
},
},
},
});
```
I've added workarounds to allow us to bypass things such as batching and
invalidating queries. But I think we should instead remove this and
update all the mutations where a query is required for a more optimised
system.
## Example benchmarks
Using stg-app vs this preview there's an average 50% speed increase
across mutations.
**Set signer step:**
Average old speed: ~1100ms
Average new speed: ~550ms
**Set recipient step:**
Average old speed: ~1200ms
Average new speed: ~600ms
**Set fields step:**
Average old speed: ~1200ms
Average new speed: ~600ms
## Related Issue
This will resolve#470
## Changes Made
- Added ability to skip batch queries
- Added a state to store the required document data.
- Refetch the data between steps if/when required
- Optimise mutations and queries
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have followed the project's coding style guidelines.
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
## Description
Add support to login with passkeys.
Passkeys can be added via the user security settings page.
Note: Currently left out adding the type of authentication method for
the 'user security audit logs' because we're using the `signIn`
next-auth event which doesn't appear to provide the context. Will look
into it at another time.
## Changes Made
- Add passkeys to login
- Add passkeys feature flag
- Add page to manage passkeys
- Add audit logs relating to passkeys
- Updated prisma schema to support passkeys & anonymous verification
tokens
## Testing Performed
To be done.
MacOS:
- Safari ✅
- Chrome ✅
- Firefox ✅
Windows:
- Chrome [Untested]
- Firefox [Untested]
Linux:
- Chrome [Untested]
- Firefox [Untested]
iOS:
- Safari ✅
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [X] I have tested these changes locally and they work as expected.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced Passkey authentication, including creation, sign-in, and
management of passkeys.
- Added a Passkeys section in Security Settings for managing user
passkeys.
- Implemented UI updates for Passkey authentication, including a new
dialog for creating passkeys and a data table for managing them.
- Enhanced security settings with server-side feature flags to
conditionally display new security features.
- **Bug Fixes**
- Improved UI consistency in the Settings Security Activity Page.
- Updated button styling in the 2FA Recovery Codes component for better
visibility.
- **Refactor**
- Streamlined authentication options to include WebAuthn credentials
provider.
- **Chores**
- Updated database schema to support passkeys and related functionality.
- Added new audit log types for passkey-related activities.
- Enhanced server-only authentication utilities for passkey registration
and management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Details
Currently there's no way to have a proper PR template selector. Since we
rarely use the E2E template, I've opted to move the generic PR template
to be the default template.
## Description
Update the add signer form to disable the signers when required.
I assume the actual issue is that `{...field}` was spreading a disabled
prop which was overriding our one.
## Changes Made
- Use fieldset to disable inputs
- Manually disable select since fieldset doesn't work for that select
for some reason
Right now, the eslint command runs separately for each staged file. This
PR aims to change that by running just one eslint command for all the
files that have been changed.
## Description
Updates the `on-create.sh` script to use our `dx` command resolving
issues where it couldn't find the docker compose file due to changes
that have happened since publishing the Documenso docker image.
## Related Issue
Resolves#1026
## Test Details
N/A
## Checklist
- [x] I have written the new test and ensured it works as intended.
- [x] I have added necessary documentation to explain the purpose of the
test.
- [x] I have followed the project's testing guidelines and coding style.
- [x] I have addressed any review feedback from previous submissions, if
applicable.
## Additional Notes
N/A
## Description
Updates the docker documentation and compose files to include key file
related configuration for self hosters. This was missed as the key file
environment variables weren't documented in the `.env.example` file
which was used for creating the docker documentation.
## Related Issue
#1012
## Changes Made
- Updated the docker configuration to include key file environment
variables
- Updated the .env.example to include key file env vars
- Updated the compose images to use volumes for key files
## Testing Performed
- I have used the containers with the updated values and confirmed that
they work as expected
## Checklist
- [x] I have tested these changes locally and they work as expected.
- [x] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [x] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
N/A
## Description
This pull request addresses the issue where users on touchscreen devices
couldn't properly see the Recipients list. I have implemented a two-easy
solution to address this problem, offering a seamless user experience
across device types:
1. Popover for Touchscreen: For touchscreen devices, I have added a
popover that displays the Recipients list when users tap on the avatars.
2. Tooltip for Larger Screens: On larger screens (desktops and laptops),
I have added a tooltip that appears when users hover over the avatars.
## Changes
1. Added `Popover` for smaller devices and keep the `Tooltip` for larger
devices.
2. Renamed the component `stack-avatars-wtih-tooltip` to
`stack-avatars-wtih-ui` because now it contains both Tooltip and
Popover.
3. Used the `useWindowSize` hook to conditionally render the `Tooltip`
and `Popover`
4. To avoid repeating the same code, I've created a new component named
`stack-avatars-components.tsx` to show the recipient's details. This
component uses both Popover and Tooltip.
## PR Preview
https://github.com/documenso/documenso/assets/87828904/2dc9b056-b4bd-43dd-b427-a0e803dee55a
## Issue
Closes#756
When prisma:migrate-dev needs to reset the database it will run the seed script to repopulate data. Now that we've added the seed script to our root setup command we will want to avoid this behaviour since we will end up double seeding the database which currently can cause issues.
## Description
Fixes the issue with Vercel preview deployments failing.
It appears that the old `PGHOST` environment variable injected by neon
was:
`ep-snowy-snowflake-a2vc5pa2.eu-central-1.aws.neon.tech`
It is now:
`ep-snowy-snowflake-a2vc5pa2.eu-central-1-pooler.aws.neon.tech`
Notice the `-pooler` being attached automatically to the `PGHOST`.
## References
> The following changes were made to the [Neon Vercel
Integration](https://vercel.com/integrations/neon):
>
>To ensure that users accessing a Neon database from a serverless
environment have enough connections, the DATABASE_URL and PGHOST
environment variables added to a Vercel project by the Neon integration
are now set to a pooled Neon connection string by default. Pooled
connections support up to 10,000 simultaneous connections. Previously,
these variables were set to an unpooled connection string supporting
fewer concurrent connections.
https://neon.tech/docs/changeloghttps://neon.tech/docs/guides/vercel#manage-vercel-environment-variables
Remove `bcrypt` in favor of `@node-rs/bcrypt` which includes precompiled
binaries for many platforms reducing the number of tasks to run post
`npm install`.
Resolves#986
Tidy the docker setup and include a Github Action
for publishing docker containers to DockerHub and
Github Container Registry.
Also add a small README file with docker hosting instructions.
Tidy the docker setup and include a Github Action
for publishing docker containers to DockerHub and
Github Container Registry.
Also add a small README file with docker hosting instructions.
Return the recipient as an array to match the other formats for Zapier.
Otherwise, Zaps with the "DOCUMENT_OPENED" hooks won't work.
All the other webhooks return the "Recipient" field as an array.
---
name: Pull Request
about: Submit changes to the project for review and inclusion
---
## Description
<!--- Describe the changes introduced by this pull request. -->
<!--- Explain what problem it solves or what feature/fix it adds. -->
## Related Issue
<!--- If this pull request is related to a specific issue, reference it
here using #issue_number. -->
<!--- For example, "Fixes #123" or "Addresses #456". -->
## Changes Made
<!--- Provide a summary of the changes made in this pull request. -->
<!--- Include any relevant technical details or architecture changes.
-->
- Change 1
- Change 2
- ...
## Testing Performed
<!--- Describe the testing that you have performed to validate these
changes. -->
<!--- Include information about test cases, testing environments, and
results. -->
- Tested feature X in scenario Y.
- Ran unit tests for component Z.
- Tested on browsers A, B, and C.
- ...
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [ ] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.
## Additional Notes
<!--- Provide any additional context or notes for the reviewers. -->
<!--- This might include details about design decisions, potential
concerns, or anything else relevant. -->
This PR adds the public-facing API.

The public API will allow users to interact with Documenso
programmatically. At the moment, there are 5 available endpoints:
1. `GET /documents`
- get all documents of the user making the API call
- it accepts 2 URL query parameters - `page` and `perPage`, but they are
not mandatory
2. `GET /documents/:id`
- get a specific document of the user making the API call
- it requires 1 URL parameter, which represents the ID of the document
that needs to be retrieved
3. `DELETE /documents/:id`
- delete a specific document of the user making the API call
- it requires 1 URL parameter, which represents the ID of the document
that needs to be deleted
4. `POST /documents`
- making a POST request to this endpoint will return an S3 pre-signed
URL where you can upload the document
- it requires you to pass the file name and the file content type
5. `PATCH /documents/:id/send-for-signing`
- send a document for signing
- it allows you to pass the following:
- signer email *(required)*
- signer name *(optional)*
- field type (signature, email, name, etc.) *(required)*
- page number where to insert the field *(required)*
- page X *(required)*
- page Y *(required)*
- page width *(required)*
- page height *(required)*
- email subject *(optional)*
- email body *(optional)*
The users are authenticated through API tokens. The users can create one
or more tokens in their account settings, and each token will be
available for 1 year _(duration open to suggestions/changes)_.
Each time the user makes a request, the app checks if the token exists
and if it's valid. The app will return `401` *(unauthorized)* and the
appropriate error message if either is false. If both are true AND the
user uses the correct HTTP verb and passes the required URL parameters
and body, the API call will be successful.
Code overview:
- `@documenso/packages/lib/trpc/api-contract` - this folder contains the
`contract` and `schema` files. The `contract.ts` file describes the
structure of the API, the format of the requests and responses, and how
to authenticate your API calls, among others. The `schema.ts` file
contains the Zod schemas used in the API contract.
- `@documenso/packages/trpc/server/api-token-router` - this folder
contains the `router.rs` and `schema.ts` files. Here are the tRPC
procedures used on the frontend. That is, the user's settings page,
where the user can list/create/delete API tokens.
- `@documenso/packages/trpc/tsconfig.json` - I added `"strict": true`
because that's what's suggested in the `ts-rest` documentation.
*([source](https://ts-rest.com/docs/quickstart#create-a-contract))*
- `[...ts-rest].tsx` - This file contains the implementation of the API.
You can see the logic behind each route.
- the rest of the code represents the code for the API tokens page in
the user's settings.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added two-factor authentication form handling in profile settings.
- Introduced account deletion functionality, including Stripe customer
data removal.
- Updated UI components and styles for improved user interaction, such
as enhanced visual feedback for destructive actions.
- **Refactor**
- Improved code quality by updating import statements for type-only
imports.
- **Chores**
- Restructured the `forwardPorts` array in dev container configuration
for clarity.
- Removed a trailing comma in the `vscode` extensions list to adhere to
JSON format rules.
- **Documentation**
- Added comments to clarify the handling of undefined values in
two-factor authentication verification.
- **Database**
- Implemented a SQL migration script for creating a default deleted user
in the system.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
This feature allows us to have a dedicated page to view details for a
document.
The current `/documents/[id]` page has been moved to
`/documents/[id]/edit`.
The new `/documents/[id]` page will now display general information on
the document itself.
This will allow us to provide more details relating to a document in the
future, such as version history.
## Examples
These may be outdated as deployment failed
Login via example@documenso.com
Draft document no recipients:
https://stg-7hpkvyc5i-documenso.vercel.app/documents/446
Draft document with recipients:
https://stg-7hpkvyc5i-documenso.vercel.app/documents/440
Pending document:
https://stg-7hpkvyc5i-documenso.vercel.app/documents/447
Completed document:
https://stg-7hpkvyc5i-documenso.vercel.app/documents/448
Pending teams document:
https://stg-7hpkvyc5i-documenso.vercel.app/t/hello/documents/449/
## Checklist
<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
## Additional Notes
When a user "sends" a document (DRAFT -> PENDING), should we redirect
them to the document/id page, or keep it how it currently is and have it
redirect to `/documents` page?
Disables the generation of the blog and content pages using
generateStaticPaths to deal with a regression with routing
introduced with next-runtime-env.
## Description
Add support for enterprise billing plans.
Enterprise billing plans by default get access to everything early
adopters do:
- Unlimited teams
- Unlimited documents
They will also get additional features in the future.
## Notes
Pending webhook updates to support enterprise onboarding.
Rolled back env changes `NEXT_PUBLIC_PROJECT` since it doesn't seem to
work.
improved document-dropzone ui for small vertical screens (screens less
than 800px vertically)
Although it can still become congested on really small vertical screens,
but possibility is really low.
fixes: #840
Adding a security.txt file enables security researchers to quickly and
easily see where they can submit security issues and know that they are
being taken serious. From the proposal website:
> "When security risks in web services are discovered by independent
security researchers who understand the severity of the risk, they often
lack the channels to disclose them properly. As a result, security
issues may be left unreported. security.txt defines a standard to help
organizations define the process for security researchers to disclose
security vulnerabilities securely.”
See also https://securitytxt.org
## Description
Currently we're not restricting team transfer and email verification
tokens from flowing into the frontend.
This changes restricts it to only return the required information
instead of the whole data object.
## Description
Currently the document redirect URL feature is preventing documents from
being created unless a redirect URL is provided.
During the document edit flow, the redirect URL is hidden in an advanced
tab with the value of an empty string, which will always fail the
current Zod validation since `optional` requires undefined to pass.
There are multiple ways to fix this, but I think this is the easiest
method where we can assume an empty string is valid.
## Description
Fixed issue where setting a recipient role as CC will prevent any
further changes as it is considered as "sent" and "signed".
## Other changes
- Prevent editing document after completed
- Removed CC and Viewers from the field recipient list since they will
never be filled
- Minor UI issues
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
## Description
Add the ability to feature flag the teams feature via UI.
Also added minor UI changes
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
## Description
Fixed issue where you are prevented from removing fields for a recipient
that has multiple fields.
Example:
Recipient has 3 fields, you remove 1 and proceed to next step. In the
next step the field reappears as it was not deleted.
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
## Description
Add support for teams which will allow users to collaborate on
documents.
Teams features allows users to:
- Create, manage and transfer teams
- Manage team members
- Manage team emails
- Manage a shared team inbox and documents
These changes do NOT include the following, which are planned for a
future release:
- Team templates
- Team API
- Search menu integration
## Testing Performed
- Added E2E tests for general team management
- Added E2E tests to validate document counts
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have added/updated tests that prove the effectiveness of these
changes.
- [ ] I have updated the documentation to reflect these changes, if
applicable.
- [X] I have followed the project's coding style guidelines.
## Description
**Fixed document limitation bypassing issues through templates.**
Previously, users could bypass document restrictions by utilizing
templates even after reaching their limitations. This fix ensures that
templates will no longer function as a workaround when users reach their
document limits.
## Changes
1. imported `useLimits` hook on `data-table-templates.tsx`
2. Disabled the 'Use Template' button when the user reaches their limit.
3. Added an Alert Component on top of the templates page to notify users
that they can't use templates anymore because they have reached their
limit.
4. Used `getServerLimits` hook on `template-router` to a condition on
the server.
## Example

## Issue
Closes#883
## Description
Adds the ability to see the events relating to the account.
Event data includes:
- Device
- IP Address
- Time
- Action
Actions are:
- Profile update
- Account linked to SSO (Example user signs in with Google after
creating a email/password account)
- Enable 2FA
- Disable 2FA
- Reset password
- Update password
- Sign out
- Sign in
- Sign in fail
- Sign in 2FA fail
## Changes
- Added audit logs
- Updated 2FA dialogs to have consistent footers
- Update `/settings/security/page` layout
## Testing Performed
Tested events:

More tested events:

## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have followed the project's coding style guidelines.
## Additional Notes
- Not sure if we really want to record the sign out event or not
- Might want to design breadcrumbs for nested setting pages
## Description
Currently the count for the documents table tabs do not display the
correct values when the period filter is applied.
## Changes Made
- Updated `getStats` to support filtering on period
## Testing Performed
- Tested to see if the documents tab count were being filtered based on
the period
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have followed the project's coding style guidelines.
## Description
This pull request solves the problem where the avatar component within
the document list has the same z-index value as the header component,
causing the avatar to be above the header. When two elements have the
same z-index value, the last one takes priority!
## Related Issue
Fixes#870
## Changes Made
1. Increases the value of the header's `z-index` by `10` (the current
value is `50`
## Description
Currently we are required to ensure PII data is not passed around in
search parameters and in the open for GDPR reasons.
Allowing us to encrypt and decrypt values with expiry dates will allow
us to ensure this doesn't happen.
## Changes Made
- Added TPRC router for encryption method
## Testing Performed
- Tested encrypting and decrypting data with and without `expiredAt`
- Tested via directly accessing API and also via trpc in react
components
- Tested parsing en email search param in a page and decrypting it
successfully
## Checklist
- [X] I have tested these changes locally and they work as expected.
- [X] I have followed the project's coding style guidelines.
When you're in the `/documents` page in the dashboard, if you hover over
a draft and a completed document, you'll see different URLs.
At the moment, the shortcut tries to go to the following URL for a
completed document `/documents/{doc-id}`.
However, that's the wrong URL, since the URL for a completed doc is
`/sign/{token}` when the user is the recipient, not the one that sent
the document for signing.
If it's the document owner & the document is completed, the URL is fine
as `/documents/{doc-id}`.
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Fixed the deleting signature block issue on touchscreens, for some
reason the `onClick` event isn't working on the touchscreens that's why
I've added `onTouchEnd` event to delete the signature block when the
user clicks on it.
Extracts the API implementation to a package so we can
potentially reuse it across different applications in the
event that we move off using a Next.js API route.
Additionally tidies up the tokens page and form to be more simplified.
## Description
Currently there are various z-index values that are causing:
- Toasts to be placed behind dialog blur background
- Menu being cropped off by header
## Changes Made
- Revert `z-[1000]` back to `z-50` for the header (not exactly sure why
it was bumped)
- Refactor z-indexes over 9000 to start from 1000
- Ensure z-index of toast is higher than dialog
This PR makes a small but useful tweak to the `DeleteDocumentDialog`.
Now, the input field gets cleared whenever the dialog is opened. Here’s
what’s changed:
1. **Clear Field After Deleting**: After you delete something and open
the dialog again, it won’t show the old, deleted text anymore. It’s
clean and ready for the next delete.
2. **Type Again to Confirm**: If you type something but close the dialog
without deleting, you’ll have to type it again next time. This way, it
makes sure the user really mean to delete something and didn't do it by
mistake.
Demo Link:
See the old vs. new in action here:
https://www.loom.com/share/80eca0d3b1994f7cbcab6f222db2dbfe?sid=ebc6135c-345e-4640-b395-daff190a96e7
It’s a small change, but it makes the delete process safer and smoother.
## Description
Previously we assumed that there can only be 1 subscription per user.
However, that will soon no longer the case with the introduction of the
Teams subscription.
This PR will apply the required migrations to support multiple
subscriptions.
## Changes Made
- Updated the Prisma schema to allow for multiple `Subscriptions` per
`User`
- Added a Stripe `customerId` field to the `User` model
- Updated relevant billing sections to support multiple subscriptions
## Testing Performed
- Tested running the Prisma migration on a demo database created on the
main branch
Will require a lot of additional testing.
## Checklist
- [ ] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [X] I have followed the project's coding style guidelines.
## Additional Notes
Added the following custom SQL statement to the migration:
> DELETE FROM "Subscription" WHERE "planId" IS NULL OR "priceId" IS
NULL;
Prior to deployment this will require changes to Stripe products:
- Adding `type` meta attribute
---------
Co-authored-by: Lucas Smith <me@lucasjamessmith.me>
Currently customers with inactive/expired plans will be shown the
BillingPlans component but upon hitting the subscribe button they will
be redirected to their portal where they are unable to complete a
checkout.
To resolve this we check for expiration of their subscription and use a
normal stripe checkout instead.
* feat: add link to homepage on the complete sign page for logged in users
* feat: added ChevronLeft icon to the link
* feat: remove icon from the link
Change "Open" links to "Open Startup" to reduce confusion about how to open the web app.
Additionally add the OSS Friends link to the footer since it was dropped during some changes.
Implementation of a universal upload allowing for multiple storage backends
starting with `database` and `s3`.
Allows clients to put and retrieve files from either client or server using
a blend of client and server actions.
* feat: update document table layout
- Removed dashboard page
- Removed redundant ID column
- Moved date to first column
- Added estimated locales for SSR dates
- Fixed z-index when dragging pre-existing fields
- Refactored document flow
- Added button spinner
- Added animation for document flow slider
- Updated drag and drop fields
- Updated document flow so it adjusts to the height of the PDF
- Updated claim plan dialog
This change actually makes the authoring flow work for
the most part by tying in emailing and more.
We have also done a number of quality of life updates to
simplify the codebase overall making it easier to continue
work on the refresh.
Improve the stripe webhook endpoint by checking for
subscriptions prior to performing an update to handle
cases where accounts have no created subscription.
This can happen in sitations such as when a checkout_session has been created but the payment fails.
This feature enables automatic field placement in PDFs using placeholder text, eliminating the need for manual coordinate-based positioning. It supports two complementary workflows:
1.**Automatic detection on upload** - PDFs containing structured placeholders like `{{signature, r1}}` have fields created automatically when uploaded
2.**API placeholder positioning** - Developers can reference any text in a PDF to position fields instead of calculating coordinates
## Goals
- Allow users to prepare documents in Word/Google Docs with placeholders that become signature fields
- Reduce friction for document preparation workflows
- Provide API developers with a simpler alternative to coordinate-based field positioning
- Support documents with repeated placeholders (e.g., initials on every page)
Envelopes (documents sent for signing) should automatically expire after a configurable period, preventing recipients from completing stale documents. Expiration is tracked **per-recipient** — when a recipient's signing window lapses, the document owner is notified and can resend (extending the deadline) or cancel. The document itself stays PENDING so other recipients can continue signing.
**Settings cascade**: Organisation → Team → Document (each level can override the prior).
**Default**: 1 month from when the envelope is sent (transitions to PENDING).
---
## 1. Database Schema Changes
### 1.1 Expiration period data shape
Store expiration as a structured JSON object rather than an enum or raw milliseconds. This avoids the enum treadmill (adding `FOUR_MONTHS` later requires a migration) while keeping values validated and meaningful.
**Zod schema** (defined in `packages/lib/constants/envelope-expiration.ts`):
No Prisma enum is needed — the period is stored as `Json?` on the relevant models (see sections 1.3 and 1.4).
### 1.2 Add expiration fields to `Recipient`
```prisma
model Recipient {
// ... existing fields
expiresAt DateTime?
expirationNotifiedAt DateTime? // null = not yet notified; set when owner notification sent
@@index([expiresAt])
}
```
`expiresAt` is a computed timestamp set when the envelope transitions to PENDING (at send time). It is calculated from the effective expiration period. Storing the concrete timestamp rather than a relative duration means:
- Sweep queries are simple (`WHERE expiresAt <= NOW() AND expirationNotifiedAt IS NULL`)
- No need to re-resolve the settings cascade at query time
- The sender can see the exact deadline in the UI
- The index on `expiresAt` ensures the expiration sweep query is efficient
`expirationNotifiedAt` tracks whether the owner has already been notified about this recipient's expiration, making the notification job idempotent.
Prisma `@default` doesn't work for `Json` columns, so the application-level default (`{ unit: 'month', amount: 1 }`) is applied in `extractDerivedTeamSettings` / `extractDerivedDocumentMeta` when the value is null. The migration should backfill existing rows with `{ "unit": "month", "amount": 1 }`.
**TeamGlobalSettings** (nullable, null = inherit from org):
```prisma
model TeamGlobalSettings {
// ... existing fields
envelopeExpirationPeriod Json?
}
```
### 1.4 Add expiration period to DocumentMeta
This allows per-document override during the document editing flow:
```prisma
model DocumentMeta {
// ... existing fields
envelopeExpirationPeriod Json?
}
```
When null on DocumentMeta, the resolved team/org setting is used at send time. Validated at write time using `ZEnvelopeExpirationPeriod.nullable()`.
**Important**: `envelopeExpirationPeriod` on `DocumentMeta` is a user-facing preference that may be set during the draft editing flow. It does NOT determine the final expiration — that is resolved at send time (see section 2.3). The value stored here is just the user's selection in the document editor.
---
## 2. Expiration Period Resolution
### 2.1 Duration mapping
Add to `packages/lib/constants/envelope-expiration.ts` alongside the Zod schema:
`extractDerivedTeamSettings()` in `packages/lib/utils/teams.ts` needs **no code changes** — it iterates `Object.keys(derivedSettings)` and overrides with non-null team values at runtime. The new `envelopeExpirationPeriod` field on both `OrganisationGlobalSettings` and `TeamGlobalSettings` will be automatically picked up.
Update `extractDerivedDocumentMeta()` in `packages/lib/utils/document.ts` to include the new field:
The expiration period is **locked at send time** — when the envelope transitions to PENDING. The concrete `expiresAt` timestamp is computed for each recipient when the document is actually sent.
In `packages/lib/server-only/document/send-document.ts`:
### 2.4 Compute `expiresAt` in the direct template flow
`create-document-from-direct-template.ts` creates envelopes directly as PENDING and then calls `sendDocument` afterward. Since `sendDocument` handles setting `expiresAt` on recipients, the direct template flow doesn't need to set it directly — `sendDocument` handles it.
---
## 3. Cron Job Infrastructure (New)
The current job system is purely event-triggered. Inngest natively supports cron-triggered functions, but the local provider (used in dev and by self-hosters who don't want a third-party dependency) has no scheduling capability. This section adds cron support to the local provider to maintain feature parity.
### 3.1 Extend `JobDefinition` with cron support
Add an optional `cron` field to the trigger type in `packages/lib/jobs/client/_internal/job.ts`:
### 3.3 Local provider: poller + deterministic `BackgroundJob` IDs
Use the existing `BackgroundJob` table for multi-instance dedupe instead of advisory locks. This approach keeps implementation Prisma-only (no raw SQL), works for single-instance and multi-instance deployments, and preserves existing retry/visibility behavior.
**On `defineJob()`**: If the job has a `cron` field, register an in-process scheduler entry and start a lightweight poller (every 30s with jitter).
**Each poll tick**:
1. Evaluate whether the cron schedule has one or more due run slots since the last tick (use a real cron parser, e.g. `cron-parser`)
2. For each due slot, build a deterministic run ID from job ID + scheduled slot time
3. Create a `BackgroundJob` row with that deterministic ID using Prisma
4. If insert succeeds → enqueue via the existing local job pipeline
5. If insert fails with Prisma `P2002` (unique violation) → another node already enqueued that run, skip
| `packages/lib/jobs/client/inngest.ts` | Wire up `{ cron: ... }` in `createFunction` for cron jobs |
| `packages/lib/jobs/client/_internal/*` | Add cron helper utilities (`getDueCronSlots`, run ID generation) |
---
## 4. Expiration Processing
### 4.1 Two-job architecture
Expiration uses two jobs: a **sweep dispatcher** that runs on a cron schedule and finds expired recipients, and an **individual notification job** that handles the audit log, owner notification email, and webhook for a single recipient. This separation means:
- The sweep is lightweight and fast (just a query + N job triggers)
- Each recipient's expiration notification is independently retryable
- The individual jobs are idempotent — they check `expirationNotifiedAt IS NULL` before processing
### 4.2 Sweep job: `EXPIRE_RECIPIENTS_SWEEP_JOB`
A cron-triggered job that runs every minute to find and dispatch notifications for expired recipients.
3. Creates audit log entry with `DOCUMENT_RECIPIENT_EXPIRED` type
4. Sends email notification to the **document owner** (inline — no separate email job)
5. The document stays PENDING — the owner decides whether to resend or cancel
### 4.4 Register in job client
Add `EXPIRE_RECIPIENTS_SWEEP_JOB_DEFINITION` and `NOTIFY_RECIPIENT_EXPIRED_JOB_DEFINITION` to the job registry in `packages/lib/jobs/client.ts`.
### 4.5 Email template: Recipient Expired
Target the **document owner**:
- Subject: `Signing window expired for "{recipientName}" on "{documentTitle}"`
- Body: "The signing window for {recipientName} ({recipientEmail}) on document {title} has expired. You can resend the document to extend their deadline or cancel the document."
- Include a "View Document" link to the document page in the app
-`packages/email/template-components/template-recipient-expired.tsx` — body
### 4.6 Recipient signing guard
In the signing flow, check `recipient.expiresAt` before allowing any signing action. Note that the document stays PENDING even after recipient expiration, so the existing `status !== PENDING` guard does not block expired recipients — an explicit expiration check is required:
Use a number input + unit selector combo. This gives organisations full flexibility to configure any duration without needing schema changes for new options.
**Layout**: A horizontal group with:
- A number `<Input>` (min 1, integer)
- A `<Select>` for the unit (`day`, `week`, `month`, `year`)
- A "Never expires" toggle/checkbox that disables the duration inputs and sets the value to `{ disabled: true }`
At the team level, include an "Inherit from organisation" option that clears the value to `null`.
**Validation**: Use `ZEnvelopeExpirationPeriod` for form validation.
Add a "Default Envelope Expiration" field to the `DocumentPreferencesForm` component. At the org level, there is no "Inherit" option — it must have a concrete value (default: `{ unit: 'month', amount: 1 }`).
### 5.3 Team Settings → Document Preferences
Same field as org, but with the additional "Inherit from organisation" option (stored as `null`).
### 5.4 Document Editor → Settings Step
Add the expiration selector to `packages/ui/primitives/document-flow/add-settings.tsx` inside the "Advanced Options" accordion.
Label: **"Expiration"**
Description: _"How long recipients have to complete this document after it is sent."_
### 5.5 Recipient Signing Page — Expired State
When a recipient visits a signing link for an expired recipient:
- Redirect to `/sign/{token}/expired`
- Show a clear, non-alarming message: "Your signing window has expired. Please contact the sender for a new invitation."
- Do not show the signing form or fields
- The `isExpired` flag in `get-envelope-for-recipient-signing.ts` is derived from `recipient.expiresAt`
### 5.6 Embed Signing — Expired State
Embed signing routes handle recipient expiration by throwing `embed-recipient-expired`:
-`apps/remix/app/routes/embed+/_v0+/sign.$token.tsx` — both V1 and V2 loaders check expiration
- The embed error boundary renders an `EmbedRecipientExpired` component
- Direct templates (`direct.$token.tsx`) create fresh recipients so `isExpired` is always `false`
---
## 6. API / TRPC Changes
### 6.1 Update settings mutation schemas
-`packages/trpc/server/organisation-router/update-organisation-settings.types.ts` — add `envelopeExpirationPeriod: ZEnvelopeExpirationPeriod` (non-nullable at org level)
-`packages/trpc/server/team-router/update-team-settings.types.ts` — add `envelopeExpirationPeriod: ZEnvelopeExpirationPeriod.nullable()` (null = inherit from org)
### 6.2 Update document mutation schemas
-`packages/lib/types/document-meta.ts` — add `envelopeExpirationPeriod: ZEnvelopeExpirationPeriod.nullable()` to the meta schema
-`packages/trpc/server/document-router/create-document.types.ts` — include in meta
-`packages/trpc/server/document-router/update-document.types.ts` — include in meta
-`packages/trpc/server/document-router/distribute-document.types.ts` — include in meta
### 6.3 Expose `expiresAt` in recipient responses
Ensure `expiresAt` and `expirationNotifiedAt` are returned when fetching recipients/documents so the UI can display expiration status.
### 6.4 Webhook / API schema updates
- Recipient schema includes `expiresAt` and `expirationNotifiedAt` fields (replacing the old `expired` field)
- Update `packages/api/v1/schema.ts`, webhook payload types, zapier integration, and sample data generators
---
## 7. Edge Cases & Considerations
### 7.1 Already-sent documents
The migration should NOT retroactively expire existing recipients. `expiresAt` will be null for all existing recipients, meaning they never expire (backward-compatible).
### 7.2 Re-sending / redistributing
When `redistribute` is called on a PENDING document, `expiresAt` should be refreshed on all eligible recipients. Redistributing signals active intent, so the clock should restart.
**Implementation**: `resendDocument` refreshes `recipient.expiresAt` for all recipients that haven't signed/rejected yet.
### 7.3 Multi-recipient partial expiration
If some recipients have signed and others expire, the document stays PENDING. This is the key advantage over document-level expiration — the owner can resend to extend the expired recipients' deadlines without affecting those who've already signed.
### 7.4 Partial completion
Partial signatures are preserved. The document is not sealed/completed until all required recipients have signed (or the owner cancels).
### 7.5 Timezone handling
`expiresAt` is stored as UTC. Display in the sender's configured timezone.
### 7.6 Race condition: signing at expiration time
The signing guard checks `recipient.expiresAt` in application code before the signing operation. The notification job's guard (`expirationNotifiedAt IS NULL` + `signingStatus NOT IN (SIGNED, REJECTED)`) prevents double-notifications. If a recipient signs just before expiration, the sweep's `signingStatus` filter skips them.
### 7.7 Direct template flow
`create-document-from-direct-template.ts` creates envelopes directly as PENDING then calls `sendDocument`. Since `sendDocument` sets `recipient.expiresAt`, no special handling is needed in the direct template flow.
---
## 8. Migration Plan
1. Add Prisma schema changes (`expiresAt` + `expirationNotifiedAt` on Recipient, `Json?` fields on settings models, index)
2. Generate and run migration
3. Backfill: set `envelopeExpirationPeriod` to `{ "unit": "month", "amount": 1 }` on all existing `OrganisationGlobalSettings` rows
4. No backfill on `Recipient.expiresAt` — existing recipients keep null (never expire)
Validate that signers have at least one signature field before allowing document/envelope distribution via API, matching the existing UI behavior.
## Background
The API originally allowed distributing documents/envelopes without validating that signers had signature fields assigned. This was intentional - we thought API users might have specific flows where this flexibility was needed.
However, after running it this way for a while, we've observed that more often than not, API users inadvertently send documents without fields assigned. This causes confusion for their recipients (who receive a document with nothing to sign) and breaks their own systems expecting a completed signing flow.
## Problem
The API allowed distributing documents/envelopes even when signers had no signature fields assigned. This was inconsistent with the UI which validates this condition before allowing distribution.
## Solution
### 1. Create centralized validation helper
**File**: `packages/lib/utils/recipients.ts`
- Added `RECIPIENT_ROLES_THAT_REQUIRE_FIELDS` constant (currently only `SIGNER`)
- Added `getRecipientsWithMissingFields()` function that returns recipients missing required fields
- Uses existing `isSignatureFieldType` guard from `packages/prisma/guards/is-signature-field.ts`
Add the ability for admins to move a subscription (and its associated Stripe customerId) from one organisation to another, when viewing a user in the admin panel. The target org must be owned by the same user and must be on the free plan (no existing active subscription).
## Context & Data Model
-`Organisation` has a 1:1 optional `Subscription` and a `customerId` (Stripe customer ID, `@unique`)
-`Organisation` has a 1:1 `OrganisationClaim` that tracks entitlements (team count, member count, feature flags)
-`Subscription` also stores a redundant `customerId` and has `organisationId` (`@unique`)
- When a subscription is removed from an org, its `OrganisationClaim` should be reset to the FREE claim
- **paid → free only**: The target org must NOT have an active subscription (status ACTIVE or PAST_DUE). It must be on the free plan.
- **same owner**: Both source and target orgs must be owned by the same user (the user being viewed).
- The `customerId` must move with the subscription to the target org (cleared from source, set on target).
- The Stripe subscription object itself is NOT modified — only the DB-level mapping changes. The Stripe customer stays the same; we just reassociate it to a different org.
**Route logic (in a single `prisma.$transaction`):**
1. Fetch source org with `subscription` + `organisationClaim`
2. Fetch target org with `subscription` + `organisationClaim`
3. Validate:
- Source org has an active subscription (status `ACTIVE` or `PAST_DUE`)
- Target org does NOT have an active subscription (no subscription record, or status `INACTIVE`)
- Both orgs have the same `ownerUserId`
4. In a transaction:
a. Clear `customerId` on source org (set to `null`)
b. Set `customerId` on target org to the source's `customerId`
c. Move the `Subscription` record: update `organisationId` to target org ID
d. Copy the source org's `OrganisationClaim` entitlements to the target org's `OrganisationClaim` (`originalSubscriptionClaimId`, `teamCount`, `memberCount`, `envelopeItemCount`, `flags`)
e. Reset the source org's `OrganisationClaim` to the FREE claim (using `createOrganisationClaimUpsertData(internalClaims[INTERNAL_CLAIM_ID.FREE])` pattern from `on-subscription-deleted.ts`)
**Note on ordering:** Because `Organisation.customerId` is `@unique`, we must clear the source first, then set the target — or do both in a transaction that handles the constraint. Prisma transactions handle this correctly as they apply all writes atomically.
**Register the route:**
- Import in `packages/trpc/server/admin-router/router.ts`
| `apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx` | **Modify** | Pass `ownerUserId={user.id}` to table |
## Edge Cases & Considerations
1.**Stripe customer stays the same**: The Stripe subscription is tied to a Stripe customer. We move the `customerId` to the target org, so webhook lookups (`findFirst where customerId`) will correctly resolve to the target org going forward.
2.**`@unique` constraint on `Organisation.customerId`**: Must clear source before setting target within the transaction. Prisma interactive transactions handle this correctly.
3.**`@unique` constraint on `Subscription.organisationId`**: Since the target org should not have a subscription record, updating the existing subscription's `organisationId` to the target should work. If the target has an INACTIVE subscription record, we need to delete it first.
4.**Target org has INACTIVE subscription**: The target org might have a stale INACTIVE subscription from a previous cancellation. In this case, delete the target's old subscription record before moving the source's subscription over.
5.**Seat-based plans**: If the subscription is seat-based, the Stripe quantity may not match the target org's member count. Consider calling `syncMemberCountWithStripeSeatPlan` after the swap as a post-transaction step.
6.**OrganisationClaim transfer**: Copy `originalSubscriptionClaimId`, `teamCount`, `memberCount`, `envelopeItemCount`, and `flags` from source claim to target claim. Reset source claim to FREE.
7.**No Stripe API calls needed**: This is purely a DB-level reassociation. The Stripe subscription, customer, and payment method all remain unchanged.
Certificate and audit log pages are generated with hardcoded A4 dimensions (`PDF_SIZE_A4_72PPI`: 595×842) regardless of the actual document page sizes. When the source document uses a different page size (e.g., Letter, Legal, or custom dimensions), the certificate/audit log pages end up with a different width than the document pages. This causes problems with courts that expect uniform page dimensions throughout a PDF.
**Both width and height must match** the last page of the document so the entire PDF prints uniformly.
**Root cause**: In `seal-document.handler.ts` (lines 186-187), the certificate payload always uses:
```ts
pageWidth: PDF_SIZE_A4_72PPI.width,// 595
pageHeight: PDF_SIZE_A4_72PPI.height,// 842
```
These hardcoded values flow into `generateCertificatePdf`, `generateAuditLogPdf`, `renderCertificate`, and `renderAuditLogs` — all of which use `pageWidth`/`pageHeight` to set Konva stage dimensions and layout content.
const{width,height}=lastPage;// e.g. 612, 792 for Letter
```
Already used this way in `seal-document.handler.ts` lines 403-410 for V2 field insertion.
"Last page" = last page of the original document, before cert/audit pages are appended.
### Content Layout Adaptation
Both renderers already handle variable dimensions gracefully:
- **Width**: `render-certificate.ts:713` / `render-audit-logs.ts:588` — `Math.min(pageWidth - minimumMargin * 2, contentMaxWidth)` with `contentMaxWidth = 768`. Wider pages get more margin, narrower pages tighter margins.
- **Height**: Both renderers paginate content into pages using `groupRowsIntoPages()` which respects `pageHeight` via `maxTableHeight = pageHeight - pageTopMargin - pageBottomMargin`. Shorter pages just mean more pages; taller pages fit more rows per page.
### Playwright PDF Path — Out of Scope
The `NEXT_PRIVATE_USE_PLAYWRIGHT_PDF` toggle enables a deprecated Playwright-based PDF generation path (`get-certificate-pdf.ts`, `get-audit-logs-pdf.ts`) that also hardcodes `format: 'A4'` in `page.pdf()`. This path is **not being updated** as part of this fix:
- Both files are marked `@deprecated`
- The Konva-based path is the default and recommended path
- The Playwright path is behind a feature flag and will be removed
No changes needed. Add a code comment noting the A4 limitation if the Playwright path is ever re-enabled.
## Plan
### 1. Extend `get-page-size.ts` with `@libpdf/core` utility
Add a `getLastPageDimensions` function to the existing `packages/lib/server-only/pdf/get-page-size.ts` file. This consolidates page-size logic in one place (the file already has the legacy `@cantoo/pdf-lib` version).
**Dimension rounding**: `Math.round()` both width and height. PDF points at 72ppi are typically whole numbers; rounding avoids spurious float-precision mismatches (e.g., 612.0 vs 612.00001) that would cause unnecessary duplicate cert/audit PDF generation.
**Minimum page dimensions**: Enforce a minimum threshold (e.g., 300pt for both width and height). If either dimension falls below the minimum, fall back to A4 (595×842). The certificate and audit log renderers have headers, table rows, margins, and QR codes that require a minimum viable area.
### 2. Read last page dimensions from each envelope item's PDF
In `seal-document.handler.ts`, before generating cert/audit PDFs:
- For each `envelopeItem`, load the PDF and read the **last page's width and height** using `getLastPageDimensions`
- Use `PDF.load()` then pass the loaded doc to the utility
**Resealing consideration**: When `isResealing` is true, envelope items are remapped to use `initialData` (lines 152-158) before this point. Page-size extraction must operate on the same data source that `decorateAndSignPdf` will use. Since the `envelopeItems` array is already remapped by the time we read dimensions, reading from `envelopeItem.documentData` will naturally give the correct (initial) data. No special handling needed beyond ensuring the dimension read happens **after** the resealing remap.
### 3. Generate cert/audit PDFs per unique page size
Current flow generates one cert + one audit log doc per envelope. Change to:
1. Collect `{ width, height }` of the last page for each envelope item
2. Deduplicate by `"${width}x${height}"` key (using the already-rounded integers)
3. For each unique size, generate cert PDF and audit log PDF with those dimensions
4. Store in a `Map<string, { certificateDoc, auditLogDoc }>` keyed by `"${width}x${height}"`
For the common single-document case, this is one generation — same perf as today.
### 4. Thread the correct docs into `decorateAndSignPdf`
In the envelope item loop, look up the item's last-page dimensions in the map and pass the matching cert/audit docs. Signature of `decorateAndSignPdf` doesn't change — it still receives a single `certificateDoc` and `auditLogDoc`, just the right ones per item.
### 5. Update standalone download routes
`download-document-certificate.ts` and `download-document-audit-logs.ts` also hardcode A4:
- Both routes have `documentId` which maps to a specific envelope item
- Fetch **that specific document's** PDF data, load it, read last page width + height via `getLastPageDimensions`
- Pass `{ pageWidth, pageHeight }` to the generator
- This ensures the standalone download matches the dimensions the user would see in the sealed PDF for that document
| Playwright PDF path enabled | Remains A4 — out of scope, deprecated |
| Single-doc envelope (most common) | One generation, same perf as today |
| Multi-doc envelope, same page sizes | Dedup key matches → one generation |
| Multi-doc envelope, different sizes | One generation per unique size |
### 7. Tests
- Add assertion-based E2E test (no visual regression / reference images needed)
- Seal a Letter-size (612×792) PDF through the full flow
- Load the sealed output and assert all pages (document + cert + audit) have matching width/height
- Can be added to `envelope-alignment.spec.ts` or as a new focused test
## Implementation Steps
1.**Extend `get-page-size.ts`** — add `getLastPageDimensions(pdfDoc: PDF): { width: number; height: number }` using `@libpdf/core`, with `Math.round()` and minimum dimension enforcement
2.**In `seal-document.handler.ts`**:
a. After the resealing remap (line ~159), load each envelope item's PDF via `PDF.load()` and collect last page `{ width, height }` using `getLastPageDimensions`
b. Deduplicate by `"${width}x${height}"` key
c. Generate cert/audit PDFs per unique size (parallel via `Promise.all`)
d. In envelope item loop, look up matching cert/audit doc by size key
3.**Fix `download-document-certificate.ts`** — load the specific document's PDF, read last page dims via `getLastPageDimensions`, pass to generator
4.**Fix `download-document-audit-logs.ts`** — same as above, using the specific `documentId`'s PDF
5.**Add E2E test** — assertion-based test with a Letter-size document verifying all page dimensions match after sealing
Migrate from npm to pnpm to eliminate dependency resolution duplication issues that cause bundler problems. The current npm workspace setup results in nested `node_modules` copies that don't deduplicate reliably, requiring manual hoisting and `npm dedupe` cycles. pnpm's content-addressable store and strict symlink structure eliminates this class of problem entirely.
## Current State
- **Package manager:** npm@10.7.0 with `legacy-peer-deps=true` and `prefer-dedupe=true`
- **Workspaces:** 18 total (3 apps, 15 packages) declared in root `package.json``workspaces` field
- **Lockfile:** `package-lock.json`
- **Patches:** `patch-package` with one patch (`@ai-sdk+google-vertex+3.0.81`)
- **Overrides:** `lodash`, `pdfjs-dist`, `typescript`, `zod` in root `package.json`
- **Syncpack:** installed but unconfigured (no `.syncpackrc`)
- **Heavy duplication:** `zod` in 7 workspaces, `ts-pattern` in 9, `luxon` in 8, `react` in 6, etc.
This adds a `"packageManager"` field to root `package.json` (e.g. `"packageManager": "pnpm@10.x.x"`). Remove the existing `"engines"` npm constraint if present.
#### Step 2: Create `pnpm-workspace.yaml`
```yaml
packages:
- apps/*
- packages/*
```
Remove the `"workspaces"` field from root `package.json` — pnpm uses `pnpm-workspace.yaml` instead.
#### Step 3: Convert lockfile
```bash
pnpm import
```
This reads `package-lock.json` and generates `pnpm-lock.yaml`. After verifying, delete `package-lock.json`.
#### Step 4: Create `.npmrc` for pnpm
Replace the current `.npmrc` contents. The existing settings (`legacy-peer-deps=true`, `prefer-dedupe=true`) are npm-specific.
```ini
# Hoist packages that expect to be resolvable from any workspace.
# Start strict, add patterns here only as needed.
shamefully-hoist=true
```
> **Note:** `shamefully-hoist=true` is the pragmatic starting point. It mimics npm's flat `node_modules` layout. Once the migration is stable, this can be tightened to `hoist-pattern[]` entries for specific packages that need it, moving toward pnpm's strict isolation model.
Verify the install completes without errors. Fix any peer dependency warnings — pnpm is stricter than npm with `legacy-peer-deps=true`.
#### Step 6: Convert `overrides` to `pnpm.overrides`
In root `package.json`, move the `overrides` block under `pnpm`:
```json
{
"pnpm":{
"overrides":{
"lodash":"4.17.23",
"pdfjs-dist":"5.4.296",
"typescript":"5.6.2",
"zod":"^3.25.76"
}
}
}
```
Remove the top-level `overrides` field (that's npm-specific).
#### Step 7: Convert patch-package to pnpm patches
pnpm has native patching. Convert the existing `@ai-sdk+google-vertex+3.0.81` patch:
```bash
# Remove patch-package dependency and postinstall script
# Then use pnpm's native patching:
pnpm patch @ai-sdk/google-vertex@3.0.81
# Apply the same changes from patches/@ai-sdk+google-vertex+3.0.81.patch
pnpm patch-commit <temp-dir>
```
This adds a `pnpm.patchedDependencies` entry to root `package.json` and stores the patch in a `patches/` directory (pnpm's own format). Remove `patch-package` from dependencies and the `postinstall` script.
### Phase 2: Catalogs
#### Step 8: Identify catalog candidates
Packages duplicated across 3+ workspaces are prime candidates:
1.**base:** Add pnpm — `corepack enable pnpm` or install via `npm i -g pnpm`
2.**builder:**`turbo prune` still works with pnpm. Output structure is the same.
3.**installer:**
- Replace `npm ci` with `pnpm install --frozen-lockfile`
- Copy `pnpm-lock.yaml` and `pnpm-workspace.yaml` instead of `package-lock.json`
- Remove `patch-package` from postinstall (pnpm patches are applied natively)
4.**runner:**
- Replace `npm ci --only=production` with `pnpm install --frozen-lockfile --prod`
- Or use `pnpm deploy` for standalone output (copies only production deps to a flat directory)
Review `apps/remix/Dockerfile.pnpm` — it already exists and may have most of this solved. Reconcile with the primary `docker/Dockerfile`.
#### Step 13: Update CI workflows
Search for all `npm ci`, `npm install`, `npm run` in CI config files (`.github/workflows/`, etc.) and replace with `pnpm install --frozen-lockfile`, `pnpm run`, etc.
Ensure corepack is enabled in CI runners:
```yaml
- run:corepack enable pnpm
```
#### Step 14: Update turborepo config
Turbo works with pnpm out of the box. The `turbo.json` should not need changes. Verify `turbo prune` generates correct output with pnpm lockfile.
### Phase 5: Cleanup & Tighten
#### Step 15: Remove npm-specific tooling
- Remove `syncpack` (catalogs replace its purpose)
- Remove `"workspaces"` from root `package.json` if not already done
- Delete `package-lock.json`
- Update `.gitignore` if needed (pnpm store is outside the repo by default)
#### Step 16: Tighten hoisting (optional, future)
Once stable, replace `shamefully-hoist=true` with targeted hoist patterns:
```ini
shamefully-hoist=false
hoist-pattern[]=*eslint*
hoist-pattern[]=*prettier*
# Add others as discovered
```
This moves toward strict isolation where each package can only import what it declares. Catches phantom dependency issues. Do this incrementally — let the bundler tell you what breaks.
With catalogs and strict resolution, dependencies currently hoisted to root `package.json` for deduplication purposes can be moved back to the workspaces that actually use them. The root `package.json` should only contain tooling deps (`turbo`, `prettier`, `eslint`, etc.) and `pnpm.overrides`.
## Risks and Mitigations
1.**Phantom dependencies surface:** pnpm's strict isolation will expose imports that work today only because npm hoisted them. `shamefully-hoist=true` defers this, but tightening later will reveal them.
- **Mitigation:** Start with `shamefully-hoist=true`. Tighten incrementally after the migration is stable.
2.**Peer dependency strictness:** pnpm enforces peer deps by default. The current `.npmrc` has `legacy-peer-deps=true` which suppresses all peer dep errors.
- **Mitigation:** Run `pnpm install` and address peer dep warnings. Most will be resolvable by adding missing peer deps to the relevant workspace.
3.**Docker build breakage:** The `turbo prune` + `npm ci` pipeline is battle-tested. Switching to pnpm changes the install semantics.
- **Mitigation:** The existing `Dockerfile.pnpm` in `apps/remix/` provides a reference. Test the Docker build in CI before merging.
4.**CI cache invalidation:** Switching lockfiles invalidates all CI dependency caches.
- **Mitigation:** Update cache keys to use `pnpm-lock.yaml` hash. First CI run will be slower, subsequent runs will cache normally.
5.**Turbo + pnpm compatibility:** Turbo has first-class pnpm support, but `turbo prune` output format may differ slightly.
- **Mitigation:** Test `turbo prune --scope=@documenso/remix --docker` and verify output structure before updating Dockerfile.
## Verification Checklist
- [ ]`pnpm install` succeeds with no errors
- [ ]`pnpm run build` succeeds (all workspaces)
- [ ]`pnpm run lint` passes
- [ ]`pnpm run dev` starts correctly
- [ ] Docker build produces a working image
- [ ] E2E tests pass (`pnpm run test:e2e`)
- [ ] No duplicate package copies in `node_modules` for key deps (`zod`, `react`, `typescript`)
- [ ]`pnpm audit` shows same or better results than current npm audit
Replace the in-memory `hono-rate-limiter` with a database-backed rate limiting system using Prisma and PostgreSQL. The current in-memory approach is ineffective in multi-instance deployments since there are no sticky sessions. The new system uses **bucketed counters** (one row per key/action/time-bucket with atomic increment) to efficiently handle both high-throughput API rate limiting and granular auth/email rate limiting.
### Design Decisions
- **Bucketed counters** over row-per-request: high-throughput consumers would create thousands of rows per minute; bucketed counters reduce this to one row per key per time bucket
- **Fixed time windows**: simpler than sliding windows, the 2x burst-at-boundary scenario is acceptable for rate limiting purposes
- **Dual-key rate limiting**: per-identifier (`max`) and per-IP (`globalMax`) checked independently via separate rows with a `key` prefix (`id:` / `ip:`)
- **Accept slight over-count**: the upsert is atomic (increment + return count in one operation) but concurrent requests near the limit may both see a count just under the threshold before either commits, allowing a slight overshoot
- **Fail-open on errors**: if the rate limit DB query fails, allow the request through rather than blocking legitimate users
- **Prisma upsert** with `{ increment: 1 }` for atomic counter updates, returns the updated row so count check is a single operation
- **Application cron job** for cleanup of expired bucket rows
### Rate Limit Check Flow
```
check({ ip, identifier }) ->
1. Upsert IP row (ip:{ip} / action / bucket) with count + 1, RETURNING count
-> if globalMax is set and count >= globalMax, return { isLimited: true }
Each upsert atomically increments and returns the new count in a single operation. Both counters always increment on every check — there's no conditional logic to skip one based on the other. This keeps the implementation simple and avoids read-then-write race conditions. If only IP is provided (API rate limiting), only step 1 runs.
---
## 1. Database Schema
### 1.1 Prisma model
Add to `packages/prisma/schema.prisma` after the `Counter` model:
```prisma
model RateLimit {
key String
action String
bucket DateTime
count Int @default(1)
createdAt DateTime @default(now())
@@id([key, action, bucket])
@@index([createdAt])
}
```
- **Composite primary key** `(key, action, bucket)` serves as both the unique constraint for upserts and the lookup index
- **`key`** is prefixed: `ip:1.2.3.4` or `id:user@example.com`
- **`action`** is the rate limit action name: `auth.forgot-password`, `api.v1`, etc.
- **`bucket`** is the start of the time window, truncated to the window size (e.g., `2026-02-19T10:05:00Z` for a 5-minute bucket)
- **`createdAt` index** is for the cleanup job to efficiently delete old rows
- **`count`** starts at 1 (set by the create side of the upsert)
### 1.2 Migration
Generate with `npx prisma migrate dev --name add-rate-limits`.
Exact limits are initial values — tune based on observed traffic patterns. These should be easy to adjust.
---
## 3. Integration Points
### 3.1 Hono middleware for API routes
Create a reusable Hono middleware factory in `packages/lib/server-only/rate-limit/rate-limit-middleware.ts` that wraps the `rateLimit` checker into Hono middleware:
For auth routes (Hono handlers) and tRPC routes where rate limiting is applied inline rather than via middleware, provide helpers that handle the response formatting and headers consistently.
**Hono helper** — returns a 429 `Response` with headers if limited, or `null` if allowed:
All rate limit checks must be wrapped in try/catch. On any DB error, log the error and allow the request through. Rate limiting should never block legitimate traffic due to infrastructure issues.
### 7.2 Performance
- Each API request adds 1 upsert query (~1ms)
- Auth requests add 2 upsert queries (~2ms total)
- The composite primary key ensures all lookups and upserts are index-only operations
- No `COUNT(*)` queries — the count is stored directly in the row
### 7.3 Monitoring
Log rate limit hits at `warn` level with the action, key type (IP/identifier), and count. This provides visibility into traffic patterns and helps tune limits.
### 7.4 Testing
The rate limit module should be mockable in tests. Consider exporting the bucket computation and window parsing as standalone functions for unit testing. Integration tests can verify the upsert + count logic against a test database.
### 7.5 Future improvements
- **Redis backend**: if DB pressure from rate limiting becomes measurable, swap the Prisma upsert for Redis `INCR` + `EXPIRE` with no API changes
- **System-wide circuit breaker**: add a `systemMax` config option that counts all requests for an action regardless of key
Upgrade SimpleWebAuthn packages from v9.x to v13.x to address the deprecation of `@simplewebauthn/types` and take advantage of new features and improvements.
## Current State
The codebase currently uses:
-`@simplewebauthn/browser@9.x`
-`@simplewebauthn/server@9.x`
-`@simplewebauthn/types@9.x`
## Breaking Changes Summary (v9 → v13)
### v10.0.0 Breaking Changes
1.**Minimum Node version raised to Node v20**
2.**`generateRegistrationOptions()` now expects `Base64URLString` for `excludeCredentials` IDs** (no more `type: 'public-key'` needed)
3.**`generateAuthenticationOptions()` now expects `Base64URLString` for `allowCredentials` IDs**
4.**`credentialID` returned from verification methods is now `Base64URLString`** instead of `Uint8Array`
5.**`AuthenticatorDevice.credentialID` is now `Base64URLString`**
6.**`rpID` is now required when calling `generateAuthenticationOptions()`**
7.**`generateRegistrationOptions()` will generate random user IDs** if not provided
8.**`user.id` is treated as base64url string in `startRegistration()`**
9.**`userHandle` is treated as base64url string in `startAuthentication()`**
### v11.0.0 Breaking Changes
1.**Positional arguments in `startRegistration()` and `startAuthentication()` replaced by object**
1.**Database compatibility**: The `credentialId` is stored as `Bytes` in the database. The new API uses `Base64URLString`. We need proper conversion functions.
- **Mitigation**: Use `isoBase64URL.fromBuffer()` and `isoBase64URL.toBuffer()` for conversions
2.**Existing passkeys**: Existing passkeys should continue to work as long as conversion is done correctly.
- **Mitigation**: Test with existing passkeys after upgrade
Custom email domains configured via AWS SES can get stuck in a `PENDING` state or fail validation silently. Currently, there is **no automated verification** -- users must manually click "Sync" in the UI to check domain status. If a domain fails to validate, the only option is to delete it and recreate it, which generates new DKIM keys and requires the user to update their DNS records.
### Current Pain Points
1.**No background sync** -- Domain verification status is never checked automatically; users must manually click "Sync"
2.**Stuck domains** -- Domains can remain in `PENDING` state indefinitely with no alerting or auto-recovery
3.**Failed recovery requires DNS changes** -- Deleting and recreating a domain generates new keys, forcing the user to update DNS records
4.**No visibility into failure duration** -- There's no tracking of how long a domain has been pending
## Proposed Solution
### 1. Hourly Background Sync Job
Create a new cron job (`internal.sync-email-domains`) that runs every hour to automatically verify all `PENDING` email domains.
**Pattern:** Follow the existing `cleanup-rate-limits` cron job pattern:
-`cron: '0 * * * *'` (every hour, on the hour)
- Empty `z.object({})` schema (no payload needed)
- Register in `packages/lib/jobs/client.ts`
**Handler Logic:**
1. Query all `EmailDomain` records with `status: 'PENDING'`
2. For each domain, call `verifyEmailDomain(emailDomainId)` which:
- Calls AWS SES `GetEmailIdentityCommand` to check current verification status
- Updates DB status to `ACTIVE` if verified, keeps `PENDING` otherwise
3. Log results via `io.logger` (how many checked, how many transitioned to ACTIVE)
4. Process domains in batches to avoid overwhelming SES API rate limits
5. Add error handling per-domain so one failure doesn't stop the entire sweep
### 2. Schema Changes -- Track Pending Duration
Add a `lastVerifiedAt` column to the `EmailDomain` model to track when verification was last attempted, enabling "stale domain" detection.
**File:**`packages/prisma/schema.prisma`
```prisma
model EmailDomain {
// ... existing fields ...
lastVerifiedAt DateTime? // Last time verification was checked against SES
}
```
**Migration:** Create a new Prisma migration for this column addition.
**Updates needed:**
-`verify-email-domain.ts` -- Update `lastVerifiedAt` when verification is checked
- The sync job handler -- Use `lastVerifiedAt` to avoid re-checking domains that were just verified
### 3. Domain Re-registration (Recovery) -- Delete & Recreate in SES Without Changing Keys
Add a new "Re-register" action that deletes the SES identity and recreates it using the **same** DKIM key pair stored in the database, so the user's DNS records remain valid.
- Returns full email domain detail: all fields (except privateKey), organisation info, list of associated emails, DNS records (generated from publicKey + selector)
- Rationale: Re-registration is a recovery/operational action that deletes and recreates an SES identity. This is a privileged operation that should only be performed by platform operators, not self-service by org admins.
**Table component:**`apps/remix/app/components/tables/admin-email-domains-table.tsx` (optional -- can be inline in the route file like the documents page)
#### 3e. Automatic Re-registration in Sync Job (Optional Enhancement)
In the hourly sync job, after checking verification status, if a domain has been `PENDING` for more than 48 hours:
- Automatically call `reregisterEmailDomain()` to attempt recovery
- Log the auto-recovery attempt
- This provides a self-healing mechanism without user intervention
## Implementation Plan
### Phase 1: Background Sync Job (Core)
1. Create `sync-email-domains.ts` job definition with hourly cron
2. Create `sync-email-domains.handler.ts` with batch verification logic
3. Register job in `packages/lib/jobs/client.ts`
4. Add error handling and logging
### Phase 2: Schema Enhancement
5. Add `lastVerifiedAt` column to `EmailDomain` model
6. Create Prisma migration
7. Update `verifyEmailDomain()` to set `lastVerifiedAt` on each check
8. Update sync job to use `lastVerifiedAt` for intelligent scheduling
### Phase 3: Admin Email Domains Panel
9. Create `find-email-domains` admin TRPC route + types (list/search with pagination and status filter)
10. Create `get-email-domain` admin TRPC route + types (detail view with org info, emails, DNS records)
11. Register find + get routes in admin router under `emailDomain` namespace
12. Create admin list page (`admin+/email-domains._index.tsx`) with search, status filter, DataTable
13. Create admin detail page (`admin+/email-domains.$id.tsx`) with domain info, emails table, DNS records
14. Add "Email Domains" menu item to admin sidebar (`_layout.tsx`)
### Phase 4: Re-registration Feature
15. Extract `verifyDomainWithDKIM()` as a shared helper (if not already exported)
16. Create `reregisterEmailDomain()` service function
-`apps/remix/app/routes/_authenticated+/admin+/_layout.tsx` -- Add "Email Domains" nav item to sidebar
- New Prisma migration file
## Technical Considerations
1.**SES API Rate Limits** -- AWS SES has rate limits on `GetEmailIdentityCommand`. The sync job should process domains in batches with small delays between calls (e.g., 5-10 per batch with 1s delay).
2.**Concurrency** -- The local job provider has deterministic deduplication via SHA-256 IDs, so multiple app instances won't run the same cron tick twice.
3.**Error Isolation** -- Each domain verification in the sync job should be wrapped in try/catch so one failing domain doesn't prevent others from being checked.
4.**Re-registration Safety** -- The re-register function should be idempotent. Deleting a non-existent SES identity should be handled gracefully (already done in `deleteEmailDomain`).
5.**Private Key Security** -- The private key is encrypted at rest and should only be decrypted transiently during re-registration. It should never be logged or exposed in API responses.
6.**Feature Gating** -- The sync job should only process domains belonging to organisations with active `emailDomains` claim flags. This prevents processing domains for orgs that have downgraded.
7.**Observability** -- Add structured logging to the sync job so operations teams can monitor domain verification health across all tenants.
The `GET /api/v1/documents` endpoint does not return documents inside folders. The underlying `findDocuments()` function defaults to `folderId: null` when no `folderId` is provided, meaning only root-level documents are returned. The V1 API never passes `folderId`, so folder documents are invisible to API consumers.
Additionally, neither the list endpoint nor the single-document endpoint exposes `folderId` in the response, so consumers cannot know which folder a document belongs to.
## Root Cause
In `packages/lib/server-only/document/find-documents.ts` (line 222-226):
```ts
if(folderId!==undefined){
whereClause.folderId=folderId;
}else{
whereClause.folderId=null;// Only root documents returned
}
```
The V1 `getDocuments` handler in `packages/api/v1/implementation.ts` (line 61-70) only passes `page` and `perPage` to `findDocuments` — it never extracts or forwards a `folderId` from the query string.
## Decisions
These decisions were made during the spec interview:
1.**Fix V1 directly** — The V1 API is deprecated but still actively used. This is a quick, low-risk fix. No need to defer to a newer API.
2.**Breaking change accepted** — Returning ALL documents by default (instead of root-only) is intentional. The current root-only behavior is a bug, not a feature.
3.**No root-only query option needed** — Not all documents are in folders, so consumers can filter client-side using the `folderId` field in the response if needed.
4.**No folder existence validation** — `?folderId=nonexistent` returns empty array, not 404. Consistent with V1 list endpoint patterns.
5.**Add `folderId` to both endpoints** — Both `GET /api/v1/documents` (list) and `GET /api/v1/documents/:id` (single) will include `folderId` in the response.
6.**Top-level `skipFolderFilter` is sufficient** — The inner helper filters (`findDocumentsFilter`, `findTeamDocumentsFilter`) receive `folderId: undefined` when skip is active. Prisma ignores `undefined` values in WHERE clauses, so these inner filters will not constrain by folder. No propagation needed.
7.**Scope is minimal** — Only `folderId` support. No other filters (status, period, query, senderIds) added in this change.
Add `skipFolderFilter` to the options type and modify the WHERE clause logic:
```ts
exporttypeFindDocumentsOptions={
// ... existing fields ...
folderId?: string;
skipFolderFilter?: boolean;
};
```
Modify the folderId logic (line 222-226):
```ts
if(!skipFolderFilter){
if(folderId!==undefined){
whereClause.folderId=folderId;
}else{
whereClause.folderId=null;
}
}
```
When `skipFolderFilter` is true:
- The top-level `whereClause.folderId` is never set — no folder constraint at the top level.
- The inner helpers (`findDocumentsFilter`, `findTeamDocumentsFilter`) receive `folderId: undefined`, which Prisma ignores in WHERE objects — no folder constraint at the inner level either.
- Result: all documents returned regardless of folder.
When `skipFolderFilter` is false (default, used by UI/tRPC callers):
- Existing behavior is completely unchanged. `folderId: undefined` still defaults to root-only.
## Why `skipFolderFilter` (Option B)
Two approaches were considered:
**Option A: Change `folderId: undefined` semantics to mean "all documents"**
- Risky: would affect all callers of `findDocuments` (UI, tRPC) unless every caller is audited.
- The UI intentionally shows root-only when no folder is selected.
**Option B (chosen): Add `skipFolderFilter` boolean**
- Additive — no existing callers pass this flag, so they're unaffected.
| `GET /api/v1/documents` | Root docs only | ALL docs (root + folders) |
| `GET /api/v1/documents?folderId=abc` | Not supported | Docs in folder `abc` only |
| `GET /api/v1/documents?folderId=nonexistent` | Not supported | Empty array, 200 OK |
| `GET /api/v1/documents/:id` response | No `folderId` field | Includes `folderId` |
## Implementation Notes
-`folderId` is a `String?` on the `Envelope` model in Prisma, not a number.
- The `findDocuments` function already accepts `folderId` in its options type — it just needs the `skipFolderFilter` escape hatch.
- No need to propagate `skipFolderFilter` into `findDocumentsFilter` or `findTeamDocumentsFilter`. When `folderId` is `undefined`, those helpers embed `folderId: undefined` in their Prisma WHERE objects. Prisma strips `undefined` keys, so no folder constraint is applied. This is well-documented Prisma behavior.
- The `createDocument` endpoint already supports `folderId` in the request body (line 139-144 of schema.ts), confirming the pattern.
- The `getDocument` handler fetches from `prisma.envelope.findFirstOrThrow` which already includes `folderId` on the envelope — just needs to be added to the response mapping.
## Testing
Manual and automated test cases:
1.`GET /api/v1/documents` returns docs from root AND subfolders.
2.`GET /api/v1/documents?folderId=<valid-id>` returns only docs in that folder.
3.`GET /api/v1/documents?folderId=<nonexistent-id>` returns empty array with 200 status.
4. List response includes `folderId` field on each document (null for root docs, string for folder docs).
5.`GET /api/v1/documents/:id` response includes `folderId` field.
6. Existing UI/tRPC callers of `findDocuments` are unaffected (they don't pass `skipFolderFilter`).
7. Pagination: verify `totalPages` correctly reflects the larger result set when all docs are returned.
## Breaking Change Notice
This is a **breaking change** for existing V1 API consumers:
- **Before**: `GET /api/v1/documents` returned only root-level documents (those not in any folder).
- **After**: `GET /api/v1/documents` returns all documents regardless of folder placement.
Impact:
- Consumers paginating through results will see more documents in the total count.
- Consumers building UIs will now display folder documents they previously didn't see.
- The new `folderId` field is additive and won't break existing response parsing.
This is considered a **bug fix**, not a feature removal. The previous behavior silently hid documents from API consumers.
description: Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.
allowed-tools: Bash(agent-browser:*)
---
# Browser Automation with agent-browser
## Core Workflow
Every browser automation follows this pattern:
1.**Navigate**: `agent-browser open <url>`
2.**Snapshot**: `agent-browser snapshot -i` (get element refs like `@e1`, `@e2`)
3.**Interact**: Use refs to click, fill, select
4.**Re-snapshot**: After navigation or DOM changes, get fresh refs
- Use lowercase with dashes for directories (e.g., components/auth-wizard)
- Favor named exports for components
TypeScript Usage:
- Use TypeScript for all code; prefer types over interfaces
- Use functional components with TypeScript interfaces
Syntax and Formatting:
- Create functions using `const fn = () => {}`
- Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
- Use declarative JSX
- Never use 'use client'
- Never use 1 line if statements
Error Handling and Validation:
- Prioritize error handling: handle errors and edge cases early
- Use early returns and guard clauses
- Implement proper error logging and user-friendly messages
- Use Zod for form validation
- Model expected errors as return values in Server Actions
- Use error boundaries for unexpected errors
UI and Styling:
- Use Shadcn UI, Radix, and Tailwind Aria for components and styling
- Implement responsive design with Tailwind CSS; use a mobile-first approach
- When using Lucide icons, prefer the longhand names, for example HomeIcon instead of Home
React forms
- Use zod for form validation react-hook-form for forms
- Look at TeamCreateDialog.tsx as an example of form usage
- Use <Form> <FormItem> elements, and also wrap the contents of form in a fieldset which should have the :disabled attribute when the form is loading
TRPC Specifics
- Every route should be in it's own file, example routers/teams/create-team.ts
- Every route should have a types file associated with it, example routers/teams/create-team.types.ts. These files should have the OpenAPI meta, and request/response zod schemas
- The request/response schemas should be named like Z[RouteName]RequestSchema and Z[RouteName]ResponseSchema
- Use create-team.ts and create-team.types.ts as an example when creating new routes.
- When creating the OpenAPI meta, only use GET and POST requests, do not use any other REST methods
- Deconstruct the input argument on it's one line of code.
Toast usage
- Use the t`string` macro from @lingui/react/macro to display toast messages
Remix/ReactRouter Usage
- Use (params: Route.Params) to get the params from the route
- Use (loaderData: Route.LoaderData) to get the loader data from the route
- When using loaderdata, deconstruct the data you need from the loader data inside the function body
- Do not use json() to return data, directly return the data
Translations
- Use <Trans>string</Trans> to display translations in jsx code, this should be imported from @lingui/react/macro
- Use the t`string` macro from @lingui/react/macro to display translations in typescript code
- t should be imported as const { t } = useLingui() where useLingui is imported from @lingui/react/macro
- String in constants should be using the t`string` macro
description:Create a bug report to help us improve
body:
- type:markdown
attributes:
value:Thank you for reporting an issue.
Please fill in as much of the form below as you're able to.
- type:textarea
attributes:
label:Issue Description
description:Please provide a clear and concise description of the problem.
- type:textarea
attributes:
label:Steps to Reproduce
description:Please provide step-by-step instructions to reproduce the issue. Include code snippets, error messages, and any other relevant information.
- type:textarea
attributes:
label:Expected Behavior
description:Describe what you expected to happen.
- type:textarea
attributes:
label:Current Behavior
description:Describe what is currently happening.
- type:textarea
attributes:
label:Screenshots (optional)
description:If applicable, add screenshots to help explain the issue.
- type:input
attributes:
label:Operating System [e.g., Windows 10]
- type:input
attributes:
label:Browser [e.g., Chrome, Firefox]
- type:input
attributes:
label:Version [e.g., 2.0.1]
- type:checkboxes
attributes:
label:Please check the boxes that apply to this issue report.
options:
- label:I have searched the existing issues to make sure this is not a duplicate.
- label:I have provided steps to reproduce the issue.
- label:I have included relevant environment information.
- label:I have included any relevant screenshots.
- label:I understand that this is a voluntary contribution and that there is no guarantee of resolution.
- label:I want to work on creating a PR for this issue if approved
description:Suggest a new idea or enhancement for this project
body:
- type:markdown
attributes:
value:Please provide a clear and concise title for your feature request
- type:textarea
attributes:
label:Feature Description
description:Describe the feature you are requesting in detail. Explain what problem it solves or what value it adds to the project.
- type:textarea
attributes:
label:Use Case
description:Provide a scenario or use case where this feature would be beneficial. Explain how users would interact with this feature and why it's important.
- type:textarea
attributes:
label:Proposed Solution
description:If you have an idea of how this feature could be implemented, describe it here. Include any technical details, UI/UX considerations, or design suggestions.
- type:textarea
attributes:
label:Alternatives (optional)
description:Are there any alternative ways to achieve the same goal? Describe other approaches that could be considered if this feature is not implemented.
- type:textarea
attributes:
label:Additional Context
description:Add any additional context or information that might be relevant to the feature request.
- type:checkboxes
attributes:
label:Please check the boxes that apply to this feature request.
options:
- label:I have searched the existing feature requests to make sure this is not a duplicate.
- label:I have provided a detailed description of the requested feature.
- label:I have explained the use case or scenario for this feature.
- label:I have included any relevant technical details or design suggestions.
- label:I understand that this is a suggestion and that there is no guarantee of implementation.
- label:I want to work on creating a PR for this issue if approved
console.log(`Issue Count For ${username}: ${issueCount}`);
if (issueCount > 3) {
let issueCountMessage = `### 🚨 Documenso Police 🚨`;
issueCountMessage += `\n@${username} has ${issueCount} open issues assigned already. Consider whether this issue should be assigned to them or left open for another contributor.`;
Hey There! and thank you for opening this pull request! 📝👋🏼
We require pull request titles to follow the [Conventional Commits Spec](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
description: Continue implementing a spec from a previous session
argument-hint: <spec-file-path>
---
You are continuing implementation of a specification that was started in a previous session. Work autonomously until the feature is complete and tests pass.
## Your Task
1.**Read the spec** at `$ARGUMENTS`
2.**Read CODE_STYLE.md** for formatting conventions
3.**Assess current state**:
- Check git status for uncommitted changes
- Run tests to see what's passing/failing (if E2E tests exist)
- Review any existing implementation
4.**Determine what remains** by comparing the spec to the current state
5.**Plan remaining work** using TodoWrite
6.**Continue implementing** until complete
## Assessing Current State
Run these commands to understand where the previous session left off:
```bash
git status # See uncommitted changes
git log --oneline -10 # See recent commits
npm run typecheck -w @documenso/remix # Check for type errors
npm run lint:fix # Check for linting issues
```
Review the code that's already been written to understand:
- Follow workspace rules for TypeScript, React, TRPC patterns, and Remix conventions
- Mark todos complete as you finish each task
- Commit logical chunks of work
### Code Quality
- No stubbed implementations
- Handle edge cases and error conditions
- Include descriptive error messages with context
- Use async/await for all I/O operations
- Use AppError class when throwing errors
- Use Zod for validation and react-hook-form for forms
### Testing
**Important**: E2E tests are time-consuming. Only write tests for non-trivial functionality.
- Write E2E tests in `packages/app-tests/e2e/` using Playwright
- Test critical user flows and edge cases
- Follow existing E2E test patterns in the codebase
- Use descriptive test names that explain what is being tested
- Skip tests for trivial changes (simple UI tweaks, minor refactors, etc.)
## Autonomous Workflow
Work continuously through these steps:
1.**Implement** - Write the code for the current task
2.**Typecheck** - Run `npm run typecheck -w @documenso/remix` to verify types
3.**Lint** - Run `npm run lint:fix` to fix linting issues
4.**Test** - If non-trivial, run E2E tests: `npm run test:dev -w @documenso/app-tests`
5.**Fix** - If tests fail, fix and re-run
6.**Repeat** - Move to next task
## Stopping Conditions
**Stop and report success when:**
- All spec requirements are implemented
- Typecheck passes
- Lint passes
- E2E tests pass (if written for non-trivial functionality)
**Stop and ask for help when:**
- The spec is ambiguous and you need clarification
- You encounter a blocking issue you cannot resolve
- You need to make a decision that significantly deviates from the spec
- External dependencies are missing
## Commands
```bash
# Type checking
npm run typecheck -w @documenso/remix
# Linting
npm run lint:fix
# E2E Tests (only for non-trivial work)
npm run test:dev -w @documenso/app-tests # Run E2E tests in dev mode
npm run test-ui:dev -w @documenso/app-tests # Run E2E tests with UI
npm run test:e2e # Run full E2E test suite
# Development
npm run dev # Start dev server
```
## Begin
Read the spec file and CODE_STYLE.md, assess the current implementation state, then continue where the previous session left off. Use TodoWrite to track your progress throughout.
Use this skill when you need to document the reasoning or justification for a decision, approach, or architectural choice. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization.
Use this skill when you need to create a new plan document for a feature, task, or project. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization.
Use this skill when you need to create a temporary note, exploration document, or scratch pad for ideas. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization.
-`npm run test:e2e` - Run E2E tests with Playwright
-`npm run test:dev -w @documenso/app-tests` - Run single E2E test in dev mode
-`npm run test-ui:dev -w @documenso/app-tests` - Run E2E tests with UI
-`npm run format` - Format code with Prettier
-`npm run dev` - Start development server for Remix app
**Important:** Do not run `npm run build` to verify changes unless explicitly asked. Builds take a long time (~2 minutes). Use `npx tsc --noEmit` for type checking specific packages if needed.
## Code Style Guidelines
- Use TypeScript for all code; prefer `type` over `interface`
- Use functional components with `const Component = () => {}`
- Never use classes; prefer functional/declarative patterns
- Use descriptive variable names with auxiliary verbs (isLoading, hasError)
- Directory names: lowercase with dashes (auth-wizard)
This document provides a high-level overview of the Documenso codebase to help humans and agents understand how the application is structured.
## Overview
Documenso is an open-source document signing platform built as a **monorepo** using npm workspaces and Turborepo. The application enables users to create, send, and sign documents electronically.
This document captures the code style, patterns, and conventions used in the Documenso codebase. It covers both enforceable rules and subjective "taste" elements that make our code consistent and maintainable.
@@ -5,23 +5,39 @@ If you plan to contribute to Documenso, please take a moment to feel awesome ✨
## Before getting started
- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
- Select and issue from [here](https://github.com/documenso/documenso/issues) or create a new one
- Consider the results from the discussion in the issue
- Select an issue from [here](https://github.com/documenso/documenso/issues) or create a new one
- Consider the results from the discussion on the issue
- Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
## 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 intention to work on it
You can then:
1. Comment on the issue with your intention to work on it
2. Begin work on the issue
Always feel free to ask questions or seek clarification on the issue.
## Developing
The development branch is <code>main</code>. All pull request should be made against this branch. If you need help getting started, [join us on Discord](https://documen.so/discord).
The development branch is <code>main</code>. All pull requests should be made against this branch. If you need help getting started, [join us on Discord](https://documen.so/discord).
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
own GitHub account and then
[clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
2. Create a new branch:
- Create a new branch (include the issue id and somthing readable):
- Create a new branch (include the issue id and something readable):
3. See the [Developer Setup](https://github.com/documenso/documenso/blob/main/README.md#developer-setup) for more setup details.
@@ -29,10 +45,60 @@ The development branch is <code>main</code>. All pull request should be made aga
## Building
> **Note**
> Please be sure that you can make a full production build before pushing code or creating PRs.
> Please ensure you can make a full production build before pushing code or creating PRs.
You can build the project with:
```bash
npm run build
```
## AI-Assisted Development with OpenCode
We use [OpenCode](https://opencode.ai) for AI-assisted development. OpenCode provides custom commands and skills to help maintain consistency and streamline common workflows.
OpenCode works with most major AI providers (Anthropic, OpenAI, Google, etc.) or you can use [Zen](https://opencode.ai/zen) for optimized coding models. Configure your preferred provider in the OpenCode settings.
> **Important**: All AI-generated code must be thoroughly reviewed by the contributor before submitting a PR. You are responsible for understanding and validating every line of code you submit. If we detect that contributors are simply throwing AI-generated code over the wall without proper review, they will be blocked from the repository.
### Getting Started
1. Install OpenCode (see [opencode.ai](https://opencode.ai) for other install methods):
```bash
curl -fsSL https://opencode.ai/install | bash
```
2. Configure your AI provider (or use Zen for optimized models)
3. Run `opencode` in the project root
### Available Commands
Use these commands in OpenCode by typing the command name:
description: Signing documents is a fundamental building block of private, economic, and government interactions. Access to easy and secure signing to participate in society should therefore be a fundamental right for everyone. The technology to enable this should be accessible and widespread.
authorName: 'Timur Ercan'
authorImage: '/blog/blog-author-timur.jpeg'
authorRole: 'Co-Founder'
date: 2023-07-13
tags:
- Manifesto
---
<figure>
<MdxNextImage
src="/blog/blog-banner-manifest.jpeg"
width="1260"
height="630"
alt="The Documenso Manifest blog banner"
/>
<figcaption className="text-center">
Documenso — The Open Source DocuSign Alternative.
</figcaption>
</figure>
Signing documents is a fundamental building block of private, economic, and government interactions. Access to easy and secure signing to participate in society should therefore be a fundamental right for everyone. The technology to enable this should be accessible and widespread.
We know that open source is the key to solving this need once and for all to benefit all humankind. Using open source kickstarts innovation by putting the open sharing of ideas and solutions first. With Documenso, we will create an open and globally accessible signing platform to empower users, customers, and developers to fulfill their needs. Documenso is built by and for the global community, listening and implementing what is needed. Being transparent with the code and the processes that use it brings trust and security to the platform.
We build Documenso for longevity and scale by embracing the capital efficiency and inclusiveness of the Commercial Open Source (COSS) movement. We are building a global commodity for the world.
# The Documenso Manifest
Signing documents is a fundamental building block of private, economic, and government interactions. Access to easy and secure signing to participate in society should therefore be a fundamental right for everyone. The technology to enable this should be accessible and widespread.
We know that open source is the key to solving this need once and for all to benefit all humankind. Using open source kickstarts innovation by putting the open sharing of ideas and solutions first. With Documenso, we will create an open and globally accessible signing platform to empower users, customers, and developers to fulfill their needs. Documenso is built by and for the global community, listening and implementing what is needed. Being transparent with the code and the processes that use it brings trust and security to the platform.
We build Documenso for longevity and scale by embracing the capital efficiency and inclusiveness of the Commercial Open Source (COSS) movement. We are building a global commodity for the world.
> This project is currently under community review and will publish it's first production release soon™.
## About Documenso
## About this project
Signing documents digitally should be fast and easy and should be the best practice for every document signed worldwide. This is technically quite easy today, but it also introduces a new party to every signature: The signing tool providers. While this is not a problem in itself, it should make us think about how we want these providers of trust to work. Documenso aims to be the world's most trusted document-signing tool. This trust is built by empowering you to self-host Documenso and review how it works under the hood.
Signing documents digitally is fast, easy and should be best practice for every document signed worldwide. This is technically quite easy today, but it also introduces a new party to every signature: The signing tool providers. While this is not a problem in itself, it should make us think about how we want these providers of trust to work. Documenso aims to be the world's most trusted document signing tool. This trust is built by empowering you to self-host Documenso and review how it works under the hood. Join us in creating the next generation of open trust infrastructure.
Join us in creating the next generation of open trust infrastructure.
The current project goal is to <b>[release a production ready version](https://github.com/documenso/documenso/milestone/1)</b> for self-hosting as soon as possible. If you want to help making that happen you can:
-Check out the first source code release in this repository and test it
-Tell us what you think in the current [Discussions](https://github.com/documenso/documenso/discussions)
-Join the [Slack Channel](https://documen.so/slack) for any questions and getting to know to other community members
-⭐ the repository to help us raise awareness
- Spread the word on Twitter, that Documenso is working towards a more open signing tool
- Fix or create [issues](https://github.com/documenso/documenso/issues), that are needed for the first production release
- Check out the first source code release in this repository and test it.
- Tell us what you think in the [Discussions](https://github.com/documenso/documenso/discussions).
-Join the [Discord server](https://documen.so/discord) for any questions and getting to know to other community members.
-⭐ the repository to help us raise awareness.
-Spread the word on Twitter that Documenso is working towards a more open signing tool.
-Fix or create [issues](https://github.com/documenso/documenso/issues), that areneeded for the first production release.
## Contributing
- To contribute please see our [contribution guide](https://github.com/documenso/documenso/blob/main/CONTRIBUTING.md).
- To contribute, please see our [contribution guide](https://github.com/documenso/documenso/blob/main/CONTRIBUTING.md).
# Tech
## Contact us
Documenso is built using awesome open source tech including:
Contact us if you are interested in our Enterprise plan for large organizations that need extra flexibility and control.
- [React-PDF for viewing PDFs](https://github.com/wojtekmaj/react-pdf)
- [PDF-Lib for PDF manipulation](https://github.com/Hopding/pdf-lib)
- [Zod for schema declaration and validation](https://zod.dev/)
- [Lucide React for icons in React app](https://lucide.dev/)
- [Framer Motion for motion library](https://www.framer.com/motion/)
- [Radix UI for component library](https://www.radix-ui.com/)
- Check out `/package.json` and `/apps/web/package.json` for more
- Support for [opensignpdf (requires Java on server)](https://github.com/open-pdf-sign) is currently planned.
<a href="https://cal.com/timurercan/enterprise-customers?utm_source=banner&utm_campaign=oss"><img alt="Book us with Cal.com" src="https://cal.com/book-with-cal-dark.svg" /></a>
- [PostgreSQL (local or remote)](https://www.postgresql.org/download/)
<!-- - Support for [opensignpdf (requires Java on server)](https://github.com/open-pdf-sign) is currently planned. -->
## Developer Quickstart
## Local Development
### Requirements
To run Documenso locally, you will need
- Node.js (v22 or above)
- Postgres SQL Database
- Docker (optional)
### Developer Quickstart
> **Note**: This is a quickstart for developers. It assumes that you have both [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/) installed on your machine.
Want to get up and running quickly? Follow these steps:
- [Clone the repository](https://help.github.com/articles/cloning-a-repository/) it to your local device.
1. [Fork this repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) to your GitHub account.
```sh
git clone https://github.com/documenso/documenso
```
After forking the repository, clone it to your local device by using the following command:
- Set up your `.env` file using the recommendations in the `.env.example` file.
- Run `npm run dx` in the root directory
- This will spin up a postgres database and inbucket mail server in docker containers.
That's it! You should now be able to access the app at http://localhost:3000
2. Set up your `.env` file using the recommendations in the `.env.example` file. Alternatively, just run `cp .env.example .env` to get started with our handpicked defaults.
Incoming mail will be available at http://localhost:9000
3. Run `npm run dx` in the root directory
Your database will also be available on port `54320`. You can connect to it using your favorite database client.
- This will spin up a postgres database and inbucket mailserver in a docker container.
4. Run `npm run dev` in the root directory
5. Want it even faster? Just use
```sh
npm run d
```
#### Access Points for Your Application
1.**App** - http://localhost:3000
2.**Incoming Mail Access** - http://localhost:9000
3.**Database Connection Details**
- **Port**: 54320
- **Connection**: Use your favorite database client to connect using the provided port.
Follow these steps to setup documenso on you local machine:
### Manual Setup
- [Clone the repository](https://help.github.com/articles/cloning-a-repository/) it to your local device.
```sh
git clone https://github.com/documenso/documenso
```
- Run <code>npm i</code> in root directory
- Rename <code>.env.example</code> to <code>.env</code>
- Set DATABASE_URL value in .env file
- You can use the provided test database url (may be wiped at any point)
- Or setup a local postgres sql instance (recommended)
- Create the database scheme by running <code>db-migrate:dev</code>
- Setup your mail provider
- Set <code>SENDGRID_API_KEY</code> value in .env file
- You need a SendGrid account, which you can create [here](https://signup.sendgrid.com/).
- Documenso uses [Nodemailer](https://nodemailer.com/about/) so you can easily use your own SMTP server by setting the <code>SMTP\_\* variables</code> in your .env
- Run <code>npm run dev</code> root directory to start
- Register a new user at http://localhost:3000/signup
Follow these steps to setup Documenso on your local machine:
1. [Fork this repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) to your GitHub account.
After forking the repository, clone it to your local device by using the following command:
3. Create your `.env` from the `.env.example`. You can use `cp .env.example .env` to get started with our handpicked defaults.
4. Set the following environment variables:
- 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
5. Create the database schema by running `npm run prisma:migrate-dev`
6. Run `npm run translate:compile` in the root directory to compile lingui
7. Run `npm run dev` in the root directory to start
8. Register a new user at http://localhost:3000/signup
---
- Optional: Seed the database using <code>npm run db-seed</code> to create a test user and document
- Optional: Upload and sign <code>apps/web/resources/example.pdf</code> manually to test your setup
- Optional: Seed the database using `npm run prisma:seed -w @documenso/prisma` to create a test user and document.
- Optional: Create your own signing certificate.
- To generate your own using these steps and a Linux Terminal or Windows Subsystem for Linux (WSL), see **[Create your own signing certificate](./SIGNING.md)**.
- Optional: Create your own signing certificate
- A demo certificate is provided in `/app/web/resources/certificate.p12`
- To generate your own using these steps and a Linux Terminal or Windows Subsystem for Linux (WSL) see **[Create your own signing certificate](#creating-your-own-signing-certificate)**.
### Run in Gitpod
## Updating
- Click below to launch a ready-to-use Gitpod workspace in your browser.
- If you pull the newest version from main, using <code>git pull</code>, it may be necessary to regenerate your database client
- You can do this by running the generate command in `/packages/prisma`:
```sh
npx prisma generate
```
- This is not necessary on first clone.
[](https://gitpod.io/#https://github.com/documenso/documenso)
# Creating your own signing certificate
### Run in DevContainer
For the digital signature of your documents you need a signing certificate in .p12 format (public and private key). You can buy one (not recommended for dev) or use the steps to create a self-signed one:
We support DevContainers for VSCode. [Click here to get started.](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/documenso/documenso)
1. Generate a private key using the OpenSSL command. You can run the following command to generate a 2048-bit RSA key:\
<code>openssl genrsa -out private.key 2048</code>
### Video walkthrough
2. Generate a self-signed certificate using the private key. You can run the following command to generate a self-signed certificate:\
This will prompt you to enter some information, such as the Common Name (CN) for the certificate. Make sure you enter the correct information. The -days parameter sets the number of days for which the certificate is valid.
3. Combine the private key and the self-signed certificate to create the p12 certificate. You can run the following command to do this: \
4. You will be prompted to enter a password for the p12 file. Choose a strong password and remember it, as you will need it to use the certificate (**can be empty for dev certificates**)
5. Place the certificate <code>/apps/web/resources/certificate.p12</code>
If you're a visual learner and prefer to watch a video walkthrough of setting up Documenso locally, check out this video:
# Docker
[](https://youtu.be/Y0ppIQrEnZs)
> We are still working on the publishing of docker images, in the meantime you can follow the steps below to create a production ready docker image.
## Docker
Want to create a production ready docker image? Follow these steps:
We provide a Docker container for Documenso, which is published on both DockerHub and GitHub Container Registry.
- Run `./docker/build.sh` in the root directory.
- Publish the image to your docker registry of choice.
You can pull the Docker image from either of these registries and run it with your preferred container hosting provider.
- Docker support
- One-Click-Deploy on Render.com Deploy
Please note that you will need to provide environment variables for connecting to the database, mailserver, and so forth.
# Troubleshooting
For detailed instructions on how to configure and run the Docker container, please refer to the [Docker README](./docker/README.md) in the `docker` directory.
## Support IPv6
## Self Hosting
In case you are deploying to a cluster that uses only IPv6. You can use a custom command to pass a parameter to the NextJS start command
We support a variety of deployment methods, and are actively working on adding more. Stay tuned for updates!
Then, inside the `documenso` folder, copy the example env file:
```
cp .env.example .env
```
The following environment variables must be set:
-`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`
> If you are using a reverse proxy in front of Documenso, don't forget to provide the public URL for the `NEXT_PUBLIC_WEBAPP_URL` variable!
Now you can install the dependencies and build it:
```
npm i
npm run build
npm run prisma:migrate-deploy
```
Finally, you can start it with:
```
cd apps/remix
npm run start
```
This will start the server on `localhost:3000`. For now, any reverse proxy can then do the frontend and SSL termination.
> If you want to run with another port than 3000, you can start the application with `next -p <ANY PORT>` from the `apps/remix` folder.
### Run as a service
You can use a systemd service file to run the app. Here is a simple example of the service running on port 3500 (using 3000 by default):
```bash
[Unit]
Description=documenso
After=network.target
[Service]
Environment=PATH=/path/to/your/node/binaries
Type=simple
User=www-data
WorkingDirectory=/var/www/documenso/apps/remix
ExecStart=/usr/bin/next start -p 3500
TimeoutSec=15
Restart=always
[Install]
WantedBy=multi-user.target
```
### Railway
[](https://railway.app/template/bG6D4p)
### Render
[](https://render.com/deploy?repo=https://github.com/documenso/documenso)
### Koyeb
[](https://app.koyeb.com/deploy?type=git&repository=github.com/documenso/documenso&branch=main&name=documenso-app&builder=dockerfile&dockerfile=/docker/Dockerfile)
## Elestio
[](https://elest.io/open-source/documenso)
## Troubleshooting
### I'm not receiving any emails when using the developer quickstart.
When using the developer quickstart, an [Inbucket](https://inbucket.org/) server will be spun up in a docker container that will store all outgoing emails locally for you to view.
The Web UI can be found at http://localhost:9000, while the SMTP port will be on localhost:2500.
### Support IPv6
If you are deploying to a cluster that uses only IPv6, You can use a custom command to pass a parameter to the Remix start command
For local docker run
@@ -227,5 +330,25 @@ containers:
- start
- --
- -H
- "::"
- '::'
```
### I can't see environment variables in my package scripts.
Wrap your package script with the `with:env` script like such:
```
npm run with:env -- npm run myscript
```
The same can be done when using `npx` for one of the bin scripts:
```
npm run with:env -- npx myscript
```
This will load environment variables from your `.env` and `.env.local` files.
For the digital signature of your documents you need a signing certificate in .p12 format (public and private key). You can buy one (not recommended for dev) or use the steps to create a self-signed one:
1. Generate a private key using the OpenSSL command. You can run the following command to generate a 2048-bit RSA key:
`openssl genrsa -out private.key 2048`
2. Generate a self-signed certificate using the private key. You can run the following command to generate a self-signed certificate:
This will prompt you to enter some information, such as the Common Name (CN) for the certificate. Make sure you enter the correct information. The `-days` parameter sets the number of days for which the certificate is valid.
3. Combine the private key and the self-signed certificate to create the p12 certificate. You can run the following commands to do this:
```bash
# Set certificate password securely (won't appear in command history)
4. **IMPORTANT**: A certificate password is required to prevent signing failures. Make sure to use a strong password (minimum 4 characters) when prompted. Certificates without passwords will cause "Failed to get private key bags" errors during document signing.
5. Place the certificate `/apps/remix/resources/certificate.p12` (If the path does not exist, it needs to be created)
## Docker
> We are still working on the publishing of docker images, in the meantime you can follow the steps below to create a production ready docker image.
Want to create a production ready docker image? Follow these steps:
- cd into `docker` directory
- Make `build.sh` executable by running `chmod +x build.sh`
- Run `./build.sh` to start building the docker image.
- Publish the image to your docker registry of choice (or) If you prefer running the image from local, run the below command
- `-d` - Let's you run the container in background
- `-p` - Passes down which ports to use. First half is the host port, Second half is the app port. You can change the first half anything you want and reverse proxy to that port.
- `-v` - Volume let's you persist the data
- `--name` - Name of the container
- `documenso:latest` - Image you have built
## Deployment
We support a variety of deployment methods, and are actively working on adding more. Stay tuned for updates!
## Railway
[](https://railway.app/template/DjrRRX)
## Render
[](https://render.com/deploy?repo=https://github.com/documenso/documenso)
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.
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).
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 is an international standard for managing information security, specifying requirements for establishing, implementing, maintaining, and continually improving an information security management system (ISMS).
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.
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.
| **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.
| **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:
| 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:
| **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.
| **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
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.
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
| **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
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.
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.
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
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
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."
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.
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
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.
| **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:
| **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
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.